~ubuntu-branches/ubuntu/natty/curl/natty-security

« back to all changes in this revision

Viewing changes to lib/imap.c

  • Committer: Bazaar Package Importer
  • Author(s): Bhavani Shankar
  • Date: 2010-06-20 13:56:28 UTC
  • mfrom: (3.4.7 sid)
  • Revision ID: james.westby@ubuntu.com-20100620135628-e30tp9jldq6hq985
Tags: 7.21.0-1ubuntu1
* Merge from debian unstable.  Remaining changes: LP: #596334
  - Keep build deps in main:
    - Drop build dependencies: stunnel, libssh2-1-dev
    - Add build-dependency on openssh-server
    - Drop libssh2-1-dev from libcurl4-openssl-dev's Depends.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *                                  _   _ ____  _
 
3
 *  Project                     ___| | | |  _ \| |
 
4
 *                             / __| | | | |_) | |
 
5
 *                            | (__| |_| |  _ <| |___
 
6
 *                             \___|\___/|_| \_\_____|
 
7
 *
 
8
 * Copyright (C) 1998 - 2010, 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 http://curl.haxx.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
 * RFC3501 IMAPv4 protocol
 
22
 * RFC5092 IMAP URL Scheme
 
23
 *
 
24
 ***************************************************************************/
 
25
 
 
26
#include "setup.h"
 
27
 
 
28
#ifndef CURL_DISABLE_IMAP
 
29
#include <stdio.h>
 
30
#include <string.h>
 
31
#include <stdlib.h>
 
32
#include <stdarg.h>
 
33
#include <ctype.h>
 
34
 
 
35
#ifdef HAVE_UNISTD_H
 
36
#include <unistd.h>
 
37
#endif
 
38
 
 
39
#ifdef HAVE_SYS_SOCKET_H
 
40
#include <sys/socket.h>
 
41
#endif
 
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
#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
 
60
#undef in_addr_t
 
61
#define in_addr_t unsigned long
 
62
#endif
 
63
 
 
64
#include <curl/curl.h>
 
65
#include "urldata.h"
 
66
#include "sendf.h"
 
67
#include "easyif.h" /* for Curl_convert_... prototypes */
 
68
 
 
69
#include "if2ip.h"
 
70
#include "hostip.h"
 
71
#include "progress.h"
 
72
#include "transfer.h"
 
73
#include "escape.h"
 
74
#include "http.h" /* for HTTP proxy tunnel stuff */
 
75
#include "socks.h"
 
76
#include "imap.h"
 
77
 
 
78
#include "strtoofft.h"
 
79
#include "strequal.h"
 
80
#include "sslgen.h"
 
81
#include "connect.h"
 
82
#include "strerror.h"
 
83
#include "select.h"
 
84
#include "multiif.h"
 
85
#include "url.h"
 
86
#include "rawstr.h"
 
87
#include "strtoofft.h"
 
88
 
 
89
#define _MPRINTF_REPLACE /* use our functions only */
 
90
#include <curl/mprintf.h>
 
91
 
 
92
#include "curl_memory.h"
 
93
/* The last #include file should be: */
 
94
#include "memdebug.h"
 
95
 
 
96
/* Local API functions */
 
97
static CURLcode imap_parse_url_path(struct connectdata *conn);
 
98
static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
 
99
static CURLcode imap_do(struct connectdata *conn, bool *done);
 
100
static CURLcode imap_done(struct connectdata *conn,
 
101
                          CURLcode, bool premature);
 
102
static CURLcode imap_connect(struct connectdata *conn, bool *done);
 
103
static CURLcode imap_disconnect(struct connectdata *conn);
 
104
static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
 
105
static int imap_getsock(struct connectdata *conn,
 
106
                        curl_socket_t *socks,
 
107
                        int numsocks);
 
108
static CURLcode imap_doing(struct connectdata *conn,
 
109
                           bool *dophase_done);
 
110
static CURLcode imap_setup_connection(struct connectdata * conn);
 
111
 
 
112
/*
 
113
 * IMAP protocol handler.
 
114
 */
 
115
 
 
116
const struct Curl_handler Curl_handler_imap = {
 
117
  "IMAP",                           /* scheme */
 
118
  imap_setup_connection,            /* setup_connection */
 
119
  imap_do,                          /* do_it */
 
120
  imap_done,                        /* done */
 
121
  ZERO_NULL,                        /* do_more */
 
122
  imap_connect,                     /* connect_it */
 
123
  imap_multi_statemach,             /* connecting */
 
124
  imap_doing,                       /* doing */
 
125
  imap_getsock,                     /* proto_getsock */
 
126
  imap_getsock,                     /* doing_getsock */
 
127
  ZERO_NULL,                        /* perform_getsock */
 
128
  imap_disconnect,                  /* disconnect */
 
129
  PORT_IMAP,                        /* defport */
 
130
  PROT_IMAP                         /* protocol */
 
131
};
 
132
 
 
133
 
 
134
#ifdef USE_SSL
 
135
/*
 
136
 * IMAPS protocol handler.
 
137
 */
 
138
 
 
139
const struct Curl_handler Curl_handler_imaps = {
 
140
  "IMAPS",                          /* scheme */
 
141
  imap_setup_connection,            /* setup_connection */
 
142
  imap_do,                          /* do_it */
 
143
  imap_done,                        /* done */
 
144
  ZERO_NULL,                        /* do_more */
 
145
  imap_connect,                     /* connect_it */
 
146
  imap_multi_statemach,             /* connecting */
 
147
  imap_doing,                       /* doing */
 
148
  imap_getsock,                     /* proto_getsock */
 
149
  imap_getsock,                     /* doing_getsock */
 
150
  ZERO_NULL,                        /* perform_getsock */
 
151
  imap_disconnect,                  /* disconnect */
 
152
  PORT_IMAPS,                       /* defport */
 
153
  PROT_IMAP | PROT_IMAPS | PROT_SSL  /* protocol */
 
154
};
 
155
#endif
 
156
 
 
157
#ifndef CURL_DISABLE_HTTP
 
158
/*
 
159
 * HTTP-proxyed IMAP protocol handler.
 
160
 */
 
161
 
 
162
static const struct Curl_handler Curl_handler_imap_proxy = {
 
163
  "IMAP",                               /* scheme */
 
164
  ZERO_NULL,                            /* setup_connection */
 
165
  Curl_http,                            /* do_it */
 
166
  Curl_http_done,                       /* done */
 
167
  ZERO_NULL,                            /* do_more */
 
168
  ZERO_NULL,                            /* connect_it */
 
169
  ZERO_NULL,                            /* connecting */
 
170
  ZERO_NULL,                            /* doing */
 
171
  ZERO_NULL,                            /* proto_getsock */
 
172
  ZERO_NULL,                            /* doing_getsock */
 
173
  ZERO_NULL,                            /* perform_getsock */
 
174
  ZERO_NULL,                            /* disconnect */
 
175
  PORT_IMAP,                            /* defport */
 
176
  PROT_HTTP                             /* protocol */
 
177
};
 
178
 
 
179
 
 
180
#ifdef USE_SSL
 
181
/*
 
182
 * HTTP-proxyed IMAPS protocol handler.
 
183
 */
 
184
 
 
185
static const struct Curl_handler Curl_handler_imaps_proxy = {
 
186
  "IMAPS",                              /* scheme */
 
187
  ZERO_NULL,                            /* setup_connection */
 
188
  Curl_http,                            /* do_it */
 
189
  Curl_http_done,                       /* done */
 
190
  ZERO_NULL,                            /* do_more */
 
191
  ZERO_NULL,                            /* connect_it */
 
192
  ZERO_NULL,                            /* connecting */
 
193
  ZERO_NULL,                            /* doing */
 
194
  ZERO_NULL,                            /* proto_getsock */
 
195
  ZERO_NULL,                            /* doing_getsock */
 
196
  ZERO_NULL,                            /* perform_getsock */
 
197
  ZERO_NULL,                            /* disconnect */
 
198
  PORT_IMAPS,                           /* defport */
 
199
  PROT_HTTP                             /* protocol */
 
200
};
 
201
#endif
 
202
#endif
 
203
 
 
204
/***********************************************************************
 
205
 *
 
206
 * imapsendf()
 
207
 *
 
208
 * Sends the formated string as an IMAP command to a server
 
209
 *
 
210
 * NOTE: we build the command in a fixed-length buffer, which sets length
 
211
 * restrictions on the command!
 
212
 *
 
213
 * Designed to never block.
 
214
 */
 
215
static CURLcode imapsendf(struct connectdata *conn,
 
216
                          const char *idstr, /* id to wait for at the
 
217
                                                completion of this command */
 
218
                          const char *fmt, ...)
 
219
{
 
220
  CURLcode res;
 
221
  struct imap_conn *imapc = &conn->proto.imapc;
 
222
  va_list ap;
 
223
  va_start(ap, fmt);
 
224
 
 
225
  imapc->idstr = idstr; /* this is the thing */
 
226
 
 
227
  res = Curl_pp_vsendf(&imapc->pp, fmt, ap);
 
228
 
 
229
  va_end(ap);
 
230
 
 
231
  return res;
 
232
}
 
233
 
 
234
static const char *getcmdid(struct connectdata *conn)
 
235
{
 
236
  static const char * const ids[]= {
 
237
    "A",
 
238
    "B",
 
239
    "C",
 
240
    "D"
 
241
  };
 
242
 
 
243
  struct imap_conn *imapc = &conn->proto.imapc;
 
244
 
 
245
  /* get the next id, but wrap at end of table */
 
246
  imapc->cmdid = (int)((imapc->cmdid+1) % (sizeof(ids)/sizeof(ids[0])));
 
247
 
 
248
  return ids[imapc->cmdid];
 
249
}
 
250
 
 
251
/* For the IMAP "protocol connect" and "doing" phases only */
 
252
static int imap_getsock(struct connectdata *conn,
 
253
                        curl_socket_t *socks,
 
254
                        int numsocks)
 
255
{
 
256
  return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
 
257
}
 
258
 
 
259
/* fucntion that checks for an imap status code at the start of the
 
260
   given string */
 
261
static int imap_endofresp(struct pingpong *pp, int *resp)
 
262
{
 
263
  char *line = pp->linestart_resp;
 
264
  size_t len = pp->nread_resp;
 
265
  struct imap_conn *imapc = &pp->conn->proto.imapc;
 
266
  const char *id = imapc->idstr;
 
267
  size_t id_len = strlen(id);
 
268
 
 
269
  if(len >= id_len + 3) {
 
270
    if(!memcmp(id, line, id_len) && (line[id_len] == ' ') ) {
 
271
      /* end of response */
 
272
      *resp = line[id_len+1]; /* O, N or B */
 
273
      return TRUE;
 
274
    }
 
275
    else if((imapc->state == IMAP_FETCH) &&
 
276
            !memcmp("* ", line, 2) ) {
 
277
      /* FETCH response we're interested in */
 
278
      *resp = '*';
 
279
      return TRUE;
 
280
    }
 
281
  }
 
282
  return FALSE; /* nothing for us */
 
283
}
 
284
 
 
285
/* This is the ONLY way to change IMAP state! */
 
286
static void state(struct connectdata *conn,
 
287
                  imapstate newstate)
 
288
{
 
289
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
 
290
  /* for debug purposes */
 
291
  static const char * const names[]={
 
292
    "STOP",
 
293
    "SERVERGREET",
 
294
    "LOGIN",
 
295
    "STARTTLS",
 
296
    "SELECT",
 
297
    "FETCH",
 
298
    "LOGOUT",
 
299
    /* LAST */
 
300
  };
 
301
#endif
 
302
  struct imap_conn *imapc = &conn->proto.imapc;
 
303
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
 
304
  if(imapc->state != newstate)
 
305
    infof(conn->data, "IMAP %p state change from %s to %s\n",
 
306
          imapc, names[imapc->state], names[newstate]);
 
307
#endif
 
308
  imapc->state = newstate;
 
309
}
 
310
 
 
311
static CURLcode imap_state_login(struct connectdata *conn)
 
312
{
 
313
  CURLcode result;
 
314
  struct FTP *imap = conn->data->state.proto.imap;
 
315
  const char *str;
 
316
 
 
317
  str = getcmdid(conn);
 
318
 
 
319
  /* send USER and password */
 
320
  result = imapsendf(conn, str, "%s LOGIN %s %s", str,
 
321
                     imap->user?imap->user:"",
 
322
                     imap->passwd?imap->passwd:"");
 
323
  if(result)
 
324
    return result;
 
325
 
 
326
  state(conn, IMAP_LOGIN);
 
327
 
 
328
  return CURLE_OK;
 
329
}
 
330
 
 
331
/* for STARTTLS responses */
 
332
static CURLcode imap_state_starttls_resp(struct connectdata *conn,
 
333
                                         int imapcode,
 
334
                                         imapstate instate)
 
335
{
 
336
  CURLcode result = CURLE_OK;
 
337
  struct SessionHandle *data = conn->data;
 
338
  (void)instate; /* no use for this yet */
 
339
 
 
340
  if(imapcode != 'O') {
 
341
    failf(data, "STARTTLS denied. %c", imapcode);
 
342
    result = CURLE_LOGIN_DENIED;
 
343
  }
 
344
  else {
 
345
    /* Curl_ssl_connect is BLOCKING */
 
346
    result = Curl_ssl_connect(conn, FIRSTSOCKET);
 
347
    if(CURLE_OK == result) {
 
348
      conn->protocol |= PROT_IMAPS;
 
349
      result = imap_state_login(conn);
 
350
    }
 
351
  }
 
352
  state(conn, IMAP_STOP);
 
353
  return result;
 
354
}
 
355
 
 
356
/* for LOGIN responses */
 
357
static CURLcode imap_state_login_resp(struct connectdata *conn,
 
358
                                      int imapcode,
 
359
                                      imapstate instate)
 
360
{
 
361
  CURLcode result = CURLE_OK;
 
362
  struct SessionHandle *data = conn->data;
 
363
  (void)instate; /* no use for this yet */
 
364
 
 
365
  if(imapcode != 'O') {
 
366
    failf(data, "Access denied. %c", imapcode);
 
367
    result = CURLE_LOGIN_DENIED;
 
368
  }
 
369
 
 
370
  state(conn, IMAP_STOP);
 
371
  return result;
 
372
}
 
373
 
 
374
/* for the (first line of) FETCH BODY[TEXT] response */
 
375
static CURLcode imap_state_fetch_resp(struct connectdata *conn,
 
376
                                      int imapcode,
 
377
                                      imapstate instate)
 
378
{
 
379
  CURLcode result = CURLE_OK;
 
380
  struct SessionHandle *data = conn->data;
 
381
  struct imap_conn *imapc = &conn->proto.imapc;
 
382
  struct FTP *imap = data->state.proto.imap;
 
383
  struct pingpong *pp = &imapc->pp;
 
384
  const char *ptr = data->state.buffer;
 
385
  (void)instate; /* no use for this yet */
 
386
 
 
387
  if('*' != imapcode) {
 
388
    Curl_pgrsSetDownloadSize(data, 0);
 
389
    state(conn, IMAP_STOP);
 
390
    return CURLE_OK;
 
391
  }
 
392
 
 
393
  /* Something like this comes "* 1 FETCH (BODY[TEXT] {2021}\r" */
 
394
  while(*ptr && (*ptr != '{'))
 
395
    ptr++;
 
396
 
 
397
  if(*ptr == '{') {
 
398
    curl_off_t filesize = curlx_strtoofft(ptr+1, NULL, 10);
 
399
    if(filesize)
 
400
      Curl_pgrsSetDownloadSize(data, filesize);
 
401
 
 
402
    infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", filesize);
 
403
 
 
404
    if(pp->cache) {
 
405
      /* At this point there is a bunch of data in the header "cache" that is
 
406
         actually body content, send it as body and then skip it. Do note
 
407
         that there may even be additional "headers" after the body. */
 
408
      size_t chunk = pp->cache_size;
 
409
 
 
410
      if(chunk > (size_t)filesize)
 
411
        /* the conversion from curl_off_t to size_t is always fine here */
 
412
        chunk = (size_t)filesize;
 
413
 
 
414
      result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
 
415
      if(result)
 
416
        return result;
 
417
 
 
418
      filesize -= chunk;
 
419
 
 
420
      /* we've now used parts of or the entire cache */
 
421
      if(pp->cache_size > chunk) {
 
422
        /* part of, move the trailing data to the start and reduce the size */
 
423
        memmove(pp->cache, pp->cache+chunk,
 
424
                pp->cache_size - chunk);
 
425
        pp->cache_size -= chunk;
 
426
      }
 
427
      else {
 
428
        /* cache is drained */
 
429
        free(pp->cache);
 
430
        pp->cache = NULL;
 
431
        pp->cache_size = 0;
 
432
      }
 
433
    }
 
434
 
 
435
    infof(data, "Filesize left: %" FORMAT_OFF_T "\n", filesize);
 
436
 
 
437
    if(!filesize)
 
438
      /* the entire data is already transfered! */
 
439
      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
 
440
    else
 
441
      /* IMAP download */
 
442
      Curl_setup_transfer(conn, FIRSTSOCKET, filesize, FALSE,
 
443
                          imap->bytecountp, -1, NULL); /* no upload here */
 
444
 
 
445
    data->req.maxdownload = filesize;
 
446
  }
 
447
  else
 
448
    /* We don't know how to parse this line */
 
449
    result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
 
450
 
 
451
  state(conn, IMAP_STOP);
 
452
  return result;
 
453
}
 
454
 
 
455
/* start the DO phase */
 
456
static CURLcode imap_select(struct connectdata *conn)
 
457
{
 
458
  CURLcode result = CURLE_OK;
 
459
  struct imap_conn *imapc = &conn->proto.imapc;
 
460
  const char *str;
 
461
 
 
462
  str = getcmdid(conn);
 
463
 
 
464
  result = imapsendf(conn, str, "%s SELECT %s", str,
 
465
                     imapc->mailbox?imapc->mailbox:"");
 
466
  if(result)
 
467
    return result;
 
468
 
 
469
  state(conn, IMAP_SELECT);
 
470
  return result;
 
471
}
 
472
 
 
473
static CURLcode imap_fetch(struct connectdata *conn)
 
474
{
 
475
  CURLcode result = CURLE_OK;
 
476
  const char *str;
 
477
 
 
478
  str = getcmdid(conn);
 
479
 
 
480
  /* TODO: make this select the correct mail
 
481
   * Use "1 body[text]" to get the full mail body of mail 1
 
482
   */
 
483
  result = imapsendf(conn, str, "%s FETCH 1 BODY[TEXT]", str);
 
484
  if(result)
 
485
    return result;
 
486
 
 
487
  /*
 
488
   * When issued, the server will respond with a single line similar to
 
489
   * '* 1 FETCH (BODY[TEXT] {2021}'
 
490
   *
 
491
   * Identifying the fetch and how many bytes of contents we can expect. We
 
492
   * must extract that number before continuing to "download as usual".
 
493
   */
 
494
 
 
495
  state(conn, IMAP_FETCH);
 
496
  return result;
 
497
}
 
498
 
 
499
/* for SELECT responses */
 
500
static CURLcode imap_state_select_resp(struct connectdata *conn,
 
501
                                       int imapcode,
 
502
                                       imapstate instate)
 
503
{
 
504
  CURLcode result = CURLE_OK;
 
505
  struct SessionHandle *data = conn->data;
 
506
  (void)instate; /* no use for this yet */
 
507
 
 
508
  if(imapcode != 'O') {
 
509
    failf(data, "Select failed");
 
510
    result = CURLE_LOGIN_DENIED;
 
511
  }
 
512
  else
 
513
    result = imap_fetch(conn);
 
514
  return result;
 
515
}
 
516
 
 
517
static CURLcode imap_statemach_act(struct connectdata *conn)
 
518
{
 
519
  CURLcode result;
 
520
  curl_socket_t sock = conn->sock[FIRSTSOCKET];
 
521
  struct SessionHandle *data=conn->data;
 
522
  int imapcode;
 
523
  struct imap_conn *imapc = &conn->proto.imapc;
 
524
  struct pingpong *pp = &imapc->pp;
 
525
  size_t nread = 0;
 
526
 
 
527
  if(pp->sendleft)
 
528
    return Curl_pp_flushsend(pp);
 
529
 
 
530
  /* we read a piece of response */
 
531
  result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
 
532
  if(result)
 
533
    return result;
 
534
 
 
535
  if(imapcode)
 
536
  /* we have now received a full IMAP server response */
 
537
  switch(imapc->state) {
 
538
  case IMAP_SERVERGREET:
 
539
    if(imapcode != 'O') {
 
540
      failf(data, "Got unexpected imap-server response");
 
541
      return CURLE_FTP_WEIRD_SERVER_REPLY;
 
542
    }
 
543
 
 
544
    if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
 
545
      /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
 
546
         to TLS connection now */
 
547
      const char *str;
 
548
 
 
549
      str = getcmdid(conn);
 
550
      result = imapsendf(conn, str, "%s STARTTLS", str);
 
551
      state(conn, IMAP_STARTTLS);
 
552
    }
 
553
    else
 
554
      result = imap_state_login(conn);
 
555
    if(result)
 
556
      return result;
 
557
    break;
 
558
 
 
559
  case IMAP_LOGIN:
 
560
    result = imap_state_login_resp(conn, imapcode, imapc->state);
 
561
    break;
 
562
 
 
563
  case IMAP_STARTTLS:
 
564
    result = imap_state_starttls_resp(conn, imapcode, imapc->state);
 
565
    break;
 
566
 
 
567
  case IMAP_FETCH:
 
568
    result = imap_state_fetch_resp(conn, imapcode, imapc->state);
 
569
    break;
 
570
 
 
571
  case IMAP_SELECT:
 
572
    result = imap_state_select_resp(conn, imapcode, imapc->state);
 
573
    break;
 
574
 
 
575
  case IMAP_LOGOUT:
 
576
    /* fallthrough, just stop! */
 
577
  default:
 
578
    /* internal error */
 
579
    state(conn, IMAP_STOP);
 
580
    break;
 
581
  }
 
582
 
 
583
  return result;
 
584
}
 
585
 
 
586
/* called repeatedly until done from multi.c */
 
587
static CURLcode imap_multi_statemach(struct connectdata *conn,
 
588
                                         bool *done)
 
589
{
 
590
  struct imap_conn *imapc = &conn->proto.imapc;
 
591
  CURLcode result = Curl_pp_multi_statemach(&imapc->pp);
 
592
 
 
593
  *done = (bool)(imapc->state == IMAP_STOP);
 
594
 
 
595
  return result;
 
596
}
 
597
 
 
598
static CURLcode imap_easy_statemach(struct connectdata *conn)
 
599
{
 
600
  struct imap_conn *imapc = &conn->proto.imapc;
 
601
  struct pingpong *pp = &imapc->pp;
 
602
  CURLcode result = CURLE_OK;
 
603
 
 
604
  while(imapc->state != IMAP_STOP) {
 
605
    result = Curl_pp_easy_statemach(pp);
 
606
    if(result)
 
607
      break;
 
608
  }
 
609
 
 
610
  return result;
 
611
}
 
612
 
 
613
/*
 
614
 * Allocate and initialize the struct IMAP for the current SessionHandle.  If
 
615
 * need be.
 
616
 */
 
617
static CURLcode imap_init(struct connectdata *conn)
 
618
{
 
619
  struct SessionHandle *data = conn->data;
 
620
  struct FTP *imap = data->state.proto.imap;
 
621
  if(!imap) {
 
622
    imap = data->state.proto.imap = calloc(sizeof(struct FTP), 1);
 
623
    if(!imap)
 
624
      return CURLE_OUT_OF_MEMORY;
 
625
  }
 
626
 
 
627
  /* get some initial data into the imap struct */
 
628
  imap->bytecountp = &data->req.bytecount;
 
629
 
 
630
  /* No need to duplicate user+password, the connectdata struct won't change
 
631
     during a session, but we re-init them here since on subsequent inits
 
632
     since the conn struct may have changed or been replaced.
 
633
  */
 
634
  imap->user = conn->user;
 
635
  imap->passwd = conn->passwd;
 
636
 
 
637
  return CURLE_OK;
 
638
}
 
639
 
 
640
/*
 
641
 * imap_connect() should do everything that is to be considered a part of
 
642
 * the connection phase.
 
643
 *
 
644
 * The variable 'done' points to will be TRUE if the protocol-layer connect
 
645
 * phase is done when this function returns, or FALSE is not. When called as
 
646
 * a part of the easy interface, it will always be TRUE.
 
647
 */
 
648
static CURLcode imap_connect(struct connectdata *conn,
 
649
                                 bool *done) /* see description above */
 
650
{
 
651
  CURLcode result;
 
652
  struct imap_conn *imapc = &conn->proto.imapc;
 
653
  struct SessionHandle *data=conn->data;
 
654
  struct pingpong *pp = &imapc->pp;
 
655
 
 
656
  *done = FALSE; /* default to not done yet */
 
657
 
 
658
  /* If there already is a protocol-specific struct allocated for this
 
659
     sessionhandle, deal with it */
 
660
  Curl_reset_reqproto(conn);
 
661
 
 
662
  result = imap_init(conn);
 
663
  if(CURLE_OK != result)
 
664
    return result;
 
665
 
 
666
  /* We always support persistant connections on imap */
 
667
  conn->bits.close = FALSE;
 
668
 
 
669
  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
 
670
  pp->statemach_act = imap_statemach_act;
 
671
  pp->endofresp = imap_endofresp;
 
672
  pp->conn = conn;
 
673
 
 
674
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
 
675
  if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
 
676
    /* for IMAP over HTTP proxy */
 
677
    struct HTTP http_proxy;
 
678
    struct FTP *imap_save;
 
679
 
 
680
    /* BLOCKING */
 
681
    /* We want "seamless" IMAP operations through HTTP proxy tunnel */
 
682
 
 
683
    /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
 
684
     * conn->proto.http; we want IMAP through HTTP and we have to change the
 
685
     * member temporarily for connecting to the HTTP proxy. After
 
686
     * Curl_proxyCONNECT we have to set back the member to the original struct
 
687
     * IMAP pointer
 
688
     */
 
689
    imap_save = data->state.proto.imap;
 
690
    memset(&http_proxy, 0, sizeof(http_proxy));
 
691
    data->state.proto.http = &http_proxy;
 
692
 
 
693
    result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
 
694
                               conn->host.name, conn->remote_port);
 
695
 
 
696
    data->state.proto.imap = imap_save;
 
697
 
 
698
    if(CURLE_OK != result)
 
699
      return result;
 
700
  }
 
701
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
 
702
 
 
703
  if(conn->protocol & PROT_IMAPS) {
 
704
    /* BLOCKING */
 
705
    /* IMAPS is simply imap with SSL for the control channel */
 
706
    /* now, perform the SSL initialization for this socket */
 
707
    result = Curl_ssl_connect(conn, FIRSTSOCKET);
 
708
    if(result)
 
709
      return result;
 
710
  }
 
711
 
 
712
  Curl_pp_init(pp); /* init generic pingpong data */
 
713
 
 
714
  /* When we connect, we start in the state where we await the server greeting
 
715
     response */
 
716
  state(conn, IMAP_SERVERGREET);
 
717
  imapc->idstr = "*"; /* we start off waiting for a '*' response */
 
718
 
 
719
  if(data->state.used_interface == Curl_if_multi)
 
720
    result = imap_multi_statemach(conn, done);
 
721
  else {
 
722
    result = imap_easy_statemach(conn);
 
723
    if(!result)
 
724
      *done = TRUE;
 
725
  }
 
726
 
 
727
  return result;
 
728
}
 
729
 
 
730
/***********************************************************************
 
731
 *
 
732
 * imap_done()
 
733
 *
 
734
 * The DONE function. This does what needs to be done after a single DO has
 
735
 * performed.
 
736
 *
 
737
 * Input argument is already checked for validity.
 
738
 */
 
739
static CURLcode imap_done(struct connectdata *conn, CURLcode status,
 
740
                          bool premature)
 
741
{
 
742
  struct SessionHandle *data = conn->data;
 
743
  struct FTP *imap = data->state.proto.imap;
 
744
  CURLcode result=CURLE_OK;
 
745
  (void)premature;
 
746
 
 
747
  if(!imap)
 
748
    /* When the easy handle is removed from the multi while libcurl is still
 
749
     * trying to resolve the host name, it seems that the imap struct is not
 
750
     * yet initialized, but the removal action calls Curl_done() which calls
 
751
     * this function. So we simply return success if no imap pointer is set.
 
752
     */
 
753
    return CURLE_OK;
 
754
 
 
755
  if(status) {
 
756
    conn->bits.close = TRUE; /* marked for closure */
 
757
    result = status;      /* use the already set error code */
 
758
  }
 
759
 
 
760
  /* clear these for next connection */
 
761
  imap->transfer = FTPTRANSFER_BODY;
 
762
 
 
763
  return result;
 
764
}
 
765
 
 
766
/***********************************************************************
 
767
 *
 
768
 * imap_perform()
 
769
 *
 
770
 * This is the actual DO function for IMAP. Get a file/directory according to
 
771
 * the options previously setup.
 
772
 */
 
773
 
 
774
static
 
775
CURLcode imap_perform(struct connectdata *conn,
 
776
                     bool *connected,  /* connect status after PASV / PORT */
 
777
                     bool *dophase_done)
 
778
{
 
779
  /* this is IMAP and no proxy */
 
780
  CURLcode result=CURLE_OK;
 
781
 
 
782
  DEBUGF(infof(conn->data, "DO phase starts\n"));
 
783
 
 
784
  if(conn->data->set.opt_no_body) {
 
785
    /* requested no body means no transfer... */
 
786
    struct FTP *imap = conn->data->state.proto.imap;
 
787
    imap->transfer = FTPTRANSFER_INFO;
 
788
  }
 
789
 
 
790
  *dophase_done = FALSE; /* not done yet */
 
791
 
 
792
  /* start the first command in the DO phase */
 
793
  result = imap_select(conn);
 
794
  if(result)
 
795
    return result;
 
796
 
 
797
  /* run the state-machine */
 
798
  if(conn->data->state.used_interface == Curl_if_multi)
 
799
    result = imap_multi_statemach(conn, dophase_done);
 
800
  else {
 
801
    result = imap_easy_statemach(conn);
 
802
    *dophase_done = TRUE; /* with the easy interface we are done here */
 
803
  }
 
804
  *connected = conn->bits.tcpconnect;
 
805
 
 
806
  if(*dophase_done)
 
807
    DEBUGF(infof(conn->data, "DO phase is complete\n"));
 
808
 
 
809
  return result;
 
810
}
 
811
 
 
812
/***********************************************************************
 
813
 *
 
814
 * imap_do()
 
815
 *
 
816
 * This function is registered as 'curl_do' function. It decodes the path
 
817
 * parts etc as a wrapper to the actual DO function (imap_perform).
 
818
 *
 
819
 * The input argument is already checked for validity.
 
820
 */
 
821
static CURLcode imap_do(struct connectdata *conn, bool *done)
 
822
{
 
823
  CURLcode retcode = CURLE_OK;
 
824
 
 
825
  *done = FALSE; /* default to false */
 
826
 
 
827
  /*
 
828
    Since connections can be re-used between SessionHandles, this might be a
 
829
    connection already existing but on a fresh SessionHandle struct so we must
 
830
    make sure we have a good 'struct IMAP' to play with. For new connections,
 
831
    the struct IMAP is allocated and setup in the imap_connect() function.
 
832
  */
 
833
  Curl_reset_reqproto(conn);
 
834
  retcode = imap_init(conn);
 
835
  if(retcode)
 
836
    return retcode;
 
837
 
 
838
  retcode = imap_parse_url_path(conn);
 
839
  if(retcode)
 
840
    return retcode;
 
841
 
 
842
  retcode = imap_regular_transfer(conn, done);
 
843
 
 
844
  return retcode;
 
845
}
 
846
 
 
847
/***********************************************************************
 
848
 *
 
849
 * imap_logout()
 
850
 *
 
851
 * This should be called before calling sclose().  We should then wait for the
 
852
 * response from the server before returning. The calling code should then try
 
853
 * to close the connection.
 
854
 *
 
855
 */
 
856
static CURLcode imap_logout(struct connectdata *conn)
 
857
{
 
858
  CURLcode result = CURLE_OK;
 
859
  const char *str;
 
860
 
 
861
  str = getcmdid(conn);
 
862
 
 
863
  result = imapsendf(conn, str, "%s LOGOUT", str, NULL);
 
864
  if(result)
 
865
    return result;
 
866
  state(conn, IMAP_LOGOUT);
 
867
 
 
868
  result = imap_easy_statemach(conn);
 
869
 
 
870
  return result;
 
871
}
 
872
 
 
873
/***********************************************************************
 
874
 *
 
875
 * imap_disconnect()
 
876
 *
 
877
 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
 
878
 * resources. BLOCKING.
 
879
 */
 
880
static CURLcode imap_disconnect(struct connectdata *conn)
 
881
{
 
882
  struct imap_conn *imapc= &conn->proto.imapc;
 
883
 
 
884
  /* The IMAP session may or may not have been allocated/setup at this
 
885
     point! */
 
886
  if (imapc->pp.conn)
 
887
    (void)imap_logout(conn); /* ignore errors on the LOGOUT */
 
888
 
 
889
  Curl_pp_disconnect(&imapc->pp);
 
890
 
 
891
  Curl_safefree(imapc->mailbox);
 
892
 
 
893
  return CURLE_OK;
 
894
}
 
895
 
 
896
/***********************************************************************
 
897
 *
 
898
 * imap_parse_url_path()
 
899
 *
 
900
 * Parse the URL path into separate path components.
 
901
 *
 
902
 */
 
903
static CURLcode imap_parse_url_path(struct connectdata *conn)
 
904
{
 
905
  /* the imap struct is already inited in imap_connect() */
 
906
  struct imap_conn *imapc = &conn->proto.imapc;
 
907
  struct SessionHandle *data = conn->data;
 
908
  const char *path = data->state.path;
 
909
  int len;
 
910
 
 
911
  if(!*path)
 
912
    path = "INBOX";
 
913
 
 
914
  /* url decode the path and use this mailbox */
 
915
  imapc->mailbox = curl_easy_unescape(data, path, 0, &len);
 
916
  if(!imapc->mailbox)
 
917
    return CURLE_OUT_OF_MEMORY;
 
918
 
 
919
  return CURLE_OK;
 
920
}
 
921
 
 
922
/* call this when the DO phase has completed */
 
923
static CURLcode imap_dophase_done(struct connectdata *conn,
 
924
                                  bool connected)
 
925
{
 
926
  struct FTP *imap = conn->data->state.proto.imap;
 
927
  (void)connected;
 
928
 
 
929
  if(imap->transfer != FTPTRANSFER_BODY)
 
930
    /* no data to transfer */
 
931
    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
 
932
 
 
933
  return CURLE_OK;
 
934
}
 
935
 
 
936
/* called from multi.c while DOing */
 
937
static CURLcode imap_doing(struct connectdata *conn,
 
938
                               bool *dophase_done)
 
939
{
 
940
  CURLcode result;
 
941
  result = imap_multi_statemach(conn, dophase_done);
 
942
 
 
943
  if(*dophase_done) {
 
944
    result = imap_dophase_done(conn, FALSE /* not connected */);
 
945
 
 
946
    DEBUGF(infof(conn->data, "DO phase is complete\n"));
 
947
  }
 
948
  return result;
 
949
}
 
950
 
 
951
/***********************************************************************
 
952
 *
 
953
 * imap_regular_transfer()
 
954
 *
 
955
 * The input argument is already checked for validity.
 
956
 *
 
957
 * Performs all commands done before a regular transfer between a local and a
 
958
 * remote host.
 
959
 *
 
960
 */
 
961
static
 
962
CURLcode imap_regular_transfer(struct connectdata *conn,
 
963
                              bool *dophase_done)
 
964
{
 
965
  CURLcode result=CURLE_OK;
 
966
  bool connected=FALSE;
 
967
  struct SessionHandle *data = conn->data;
 
968
  data->req.size = -1; /* make sure this is unknown at this point */
 
969
 
 
970
  Curl_pgrsSetUploadCounter(data, 0);
 
971
  Curl_pgrsSetDownloadCounter(data, 0);
 
972
  Curl_pgrsSetUploadSize(data, 0);
 
973
  Curl_pgrsSetDownloadSize(data, 0);
 
974
 
 
975
  result = imap_perform(conn,
 
976
                        &connected, /* have we connected after PASV/PORT */
 
977
                        dophase_done); /* all commands in the DO-phase done? */
 
978
 
 
979
  if(CURLE_OK == result) {
 
980
 
 
981
    if(!*dophase_done)
 
982
      /* the DO phase has not completed yet */
 
983
      return CURLE_OK;
 
984
 
 
985
    result = imap_dophase_done(conn, connected);
 
986
    if(result)
 
987
      return result;
 
988
  }
 
989
 
 
990
  return result;
 
991
}
 
992
 
 
993
static CURLcode imap_setup_connection(struct connectdata * conn)
 
994
{
 
995
  struct SessionHandle *data = conn->data;
 
996
 
 
997
  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
 
998
    /* Unless we have asked to tunnel imap operations through the proxy, we
 
999
       switch and use HTTP operations only */
 
1000
#ifndef CURL_DISABLE_HTTP
 
1001
    if(conn->handler == &Curl_handler_imap)
 
1002
      conn->handler = &Curl_handler_imap_proxy;
 
1003
    else {
 
1004
#ifdef USE_SSL
 
1005
      conn->handler = &Curl_handler_imaps_proxy;
 
1006
#else
 
1007
      failf(data, "IMAPS not supported!");
 
1008
      return CURLE_UNSUPPORTED_PROTOCOL;
 
1009
#endif
 
1010
    }
 
1011
    /*
 
1012
     * We explicitly mark this connection as persistent here as we're doing
 
1013
     * IMAP over HTTP and thus we accidentally avoid setting this value
 
1014
     * otherwise.
 
1015
     */
 
1016
    conn->bits.close = FALSE;
 
1017
#else
 
1018
    failf(data, "IMAP over http proxy requires HTTP support built-in!");
 
1019
    return CURLE_UNSUPPORTED_PROTOCOL;
 
1020
#endif
 
1021
  }
 
1022
 
 
1023
  data->state.path++;   /* don't include the initial slash */
 
1024
 
 
1025
  return CURLE_OK;
 
1026
}
 
1027
 
 
1028
#endif /* CURL_DISABLE_IMAP */