~ubuntu-branches/ubuntu/quantal/curl/quantal

« back to all changes in this revision

Viewing changes to lib/ftp.c

  • Committer: Bazaar Package Importer
  • Author(s): Domenico Andreoli
  • Date: 2002-03-12 19:06:21 UTC
  • Revision ID: james.westby@ubuntu.com-20020312190621-iqx7k9cipo5d0ifr
Tags: upstream-7.9.5
ImportĀ upstreamĀ versionĀ 7.9.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************************
 
2
 *                                  _   _ ____  _     
 
3
 *  Project                     ___| | | |  _ \| |    
 
4
 *                             / __| | | | |_) | |    
 
5
 *                            | (__| |_| |  _ <| |___ 
 
6
 *                             \___|\___/|_| \_\_____|
 
7
 *
 
8
 * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al.
 
9
 *
 
10
 * In order to be useful for every potential user, curl and libcurl are
 
11
 * dual-licensed under the MPL and the MIT/X-derivate licenses.
 
12
 *
 
13
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 
14
 * copies of the Software, and permit persons to whom the Software is
 
15
 * furnished to do so, under the terms of the MPL or the MIT/X-derivate
 
16
 * licenses. You may pick one of these licenses.
 
17
 *
 
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 
19
 * KIND, either express or implied.
 
20
 *
 
21
 * $Id: ftp.c,v 1.130 2002/02/28 23:31:23 bagder Exp $
 
22
 *****************************************************************************/
 
23
 
 
24
#include "setup.h"
 
25
 
 
26
#include <stdio.h>
 
27
#include <string.h>
 
28
#include <stdlib.h>
 
29
#include <stdarg.h>
 
30
#include <ctype.h>
 
31
#include <errno.h>
 
32
 
 
33
#ifdef HAVE_UNISTD_H
 
34
#include <unistd.h>
 
35
#endif
 
36
#ifdef HAVE_SYS_SELECT_H
 
37
#include <sys/select.h>
 
38
#endif
 
39
 
 
40
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
 
41
#include <winsock.h>
 
42
#else /* some kind of unix */
 
43
#ifdef HAVE_SYS_SOCKET_H
 
44
#include <sys/socket.h>
 
45
#endif
 
46
#include <sys/types.h>
 
47
#ifdef HAVE_NETINET_IN_H
 
48
#include <netinet/in.h>
 
49
#endif
 
50
#ifdef HAVE_ARPA_INET_H
 
51
#include <arpa/inet.h>
 
52
#endif
 
53
#include <sys/utsname.h>
 
54
#ifdef HAVE_NETDB_H
 
55
#include <netdb.h>
 
56
#endif
 
57
#ifdef  VMS
 
58
#include <in.h>
 
59
#include <inet.h>
 
60
#endif
 
61
#endif
 
62
 
 
63
#if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__)
 
64
#include <errno.h>
 
65
#endif
 
66
 
 
67
#include <curl/curl.h>
 
68
#include "urldata.h"
 
69
#include "sendf.h"
 
70
 
 
71
#include "if2ip.h"
 
72
#include "hostip.h"
 
73
#include "progress.h"
 
74
#include "transfer.h"
 
75
#include "escape.h"
 
76
#include "http.h" /* for HTTP proxy tunnel stuff */
 
77
#include "ftp.h"
 
78
 
 
79
#ifdef KRB4
 
80
#include "security.h"
 
81
#include "krb4.h"
 
82
#endif
 
83
 
 
84
#include "strequal.h"
 
85
#include "ssluse.h"
 
86
#include "connect.h"
 
87
 
 
88
#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
 
89
#include "inet_ntoa_r.h"
 
90
#endif
 
91
 
 
92
#define _MPRINTF_REPLACE /* use our functions only */
 
93
#include <curl/mprintf.h>
 
94
 
 
95
/* The last #include file should be: */
 
96
#ifdef MALLOCDEBUG
 
97
#include "memdebug.h"
 
98
#endif
 
99
 
 
100
/* Local API functions */
 
101
static CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote);
 
102
static CURLcode ftp_cwd(struct connectdata *conn, char *path);
 
103
 
 
104
/* easy-to-use macro: */
 
105
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
 
106
 
 
107
/***********************************************************************
 
108
 *
 
109
 * AllowServerConnect()
 
110
 *
 
111
 * When we've issue the PORT command, we have told the server to connect
 
112
 * to us. This function will sit and wait here until the server has
 
113
 * connected.
 
114
 *
 
115
 */
 
116
static CURLcode AllowServerConnect(struct SessionHandle *data,
 
117
                                   struct connectdata *conn,
 
118
                                   int sock)
 
119
{
 
120
  fd_set rdset;
 
121
  struct timeval dt;
 
122
  
 
123
  FD_ZERO(&rdset);
 
124
 
 
125
  FD_SET(sock, &rdset);
 
126
 
 
127
  /* we give the server 10 seconds to connect to us */
 
128
  dt.tv_sec = 10;
 
129
  dt.tv_usec = 0;
 
130
 
 
131
  switch (select(sock+1, &rdset, NULL, NULL, &dt)) {
 
132
  case -1: /* error */
 
133
    /* let's die here */
 
134
    failf(data, "Error while waiting for server connect");
 
135
    return CURLE_FTP_PORT_FAILED;
 
136
  case 0:  /* timeout */
 
137
    /* let's die here */
 
138
    failf(data, "Timeout while waiting for server connect");
 
139
    return CURLE_FTP_PORT_FAILED;
 
140
  default:
 
141
    /* we have received data here */
 
142
    {
 
143
      int s;
 
144
      size_t size = sizeof(struct sockaddr_in);
 
145
      struct sockaddr_in add;
 
146
 
 
147
      getsockname(sock, (struct sockaddr *) &add, (socklen_t *)&size);
 
148
      s=accept(sock, (struct sockaddr *) &add, (socklen_t *)&size);
 
149
 
 
150
      sclose(sock); /* close the first socket */
 
151
 
 
152
      if (-1 == s) {
 
153
        /* DIE! */
 
154
        failf(data, "Error accept()ing server connect");
 
155
        return CURLE_FTP_PORT_FAILED;
 
156
      }
 
157
      infof(data, "Connection accepted from server\n");
 
158
 
 
159
      conn->secondarysocket = s;
 
160
    }
 
161
    break;
 
162
  }
 
163
  return CURLE_OK;
 
164
}
 
165
 
 
166
 
 
167
/* --- parse FTP server responses --- */
 
168
 
 
169
/*
 
170
 * Curl_GetFTPResponse() is supposed to be invoked after each command sent to
 
171
 * a remote FTP server. This function will wait and read all lines of the
 
172
 * response and extract the relevant return code for the invoking function.
 
173
 */
 
174
 
 
175
int Curl_GetFTPResponse(char *buf,
 
176
                        struct connectdata *conn,
 
177
                        int *ftpcode)
 
178
{
 
179
  /* Brand new implementation.
 
180
   * We cannot read just one byte per read() and then go back to select()
 
181
   * as it seems that the OpenSSL read() stuff doesn't grok that properly.
 
182
   *
 
183
   * Alas, read as much as possible, split up into lines, use the ending
 
184
   * line in a response or continue reading.  */
 
185
 
 
186
  int sockfd = conn->firstsocket;
 
187
  int nread;   /* total size read */
 
188
  int perline; /* count bytes per line */
 
189
  bool keepon=TRUE;
 
190
  ssize_t gotbytes;
 
191
  char *ptr;
 
192
  int timeout = 3600; /* default timeout in seconds */
 
193
  struct timeval interval;
 
194
  fd_set rkeepfd;
 
195
  fd_set readfd;
 
196
  struct SessionHandle *data = conn->data;
 
197
  char *line_start;
 
198
  int code=0; /* default "error code" to return */
 
199
 
 
200
#define SELECT_OK       0
 
201
#define SELECT_ERROR    1 /* select() problems */
 
202
#define SELECT_TIMEOUT  2 /* took too long */
 
203
#define SELECT_MEMORY   3 /* no available memory */
 
204
#define SELECT_CALLBACK 4 /* aborted by callback */
 
205
 
 
206
  int error = SELECT_OK;
 
207
 
 
208
  struct FTP *ftp = conn->proto.ftp;
 
209
 
 
210
  if (ftpcode)
 
211
    *ftpcode = 0; /* 0 for errors */
 
212
 
 
213
  if(data->set.timeout) {
 
214
    /* if timeout is requested, find out how much remaining time we have */
 
215
    timeout = data->set.timeout - /* timeout time */
 
216
      Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
 
217
    if(timeout <=0 ) {
 
218
      failf(data, "Transfer aborted due to timeout");
 
219
      return -SELECT_TIMEOUT; /* already too little time */
 
220
    }
 
221
  }
 
222
 
 
223
  FD_ZERO (&readfd);            /* clear it */
 
224
  FD_SET (sockfd, &readfd);     /* read socket */
 
225
 
 
226
  /* get this in a backup variable to be able to restore it on each lap in the
 
227
     select() loop */
 
228
  rkeepfd = readfd;
 
229
 
 
230
  ptr=buf;
 
231
  line_start = buf;
 
232
 
 
233
  nread=0;
 
234
  perline=0;
 
235
  keepon=TRUE;
 
236
 
 
237
  while((nread<BUFSIZE) && (keepon && !error)) {
 
238
    readfd = rkeepfd;              /* set every lap */
 
239
    interval.tv_sec = timeout;
 
240
    interval.tv_usec = 0;
 
241
 
 
242
    if(!ftp->cache)
 
243
      switch (select (sockfd+1, &readfd, NULL, NULL, &interval)) {
 
244
      case -1: /* select() error, stop reading */
 
245
        error = SELECT_ERROR;
 
246
        failf(data, "Transfer aborted due to select() error");
 
247
        break;
 
248
      case 0: /* timeout */
 
249
        error = SELECT_TIMEOUT;
 
250
        failf(data, "Transfer aborted due to timeout");
 
251
        break;
 
252
      default:
 
253
        error = SELECT_OK;
 
254
        break;
 
255
      }
 
256
    if(SELECT_OK == error) {
 
257
      /*
 
258
       * This code previously didn't use the kerberos sec_read() code
 
259
       * to read, but when we use Curl_read() it may do so. Do confirm
 
260
       * that this is still ok and then remove this comment!
 
261
       */
 
262
      if(ftp->cache) {
 
263
        /* we had data in the "cache", copy that instead of doing an actual
 
264
           read */
 
265
        memcpy(ptr, ftp->cache, ftp->cache_size);
 
266
        gotbytes = ftp->cache_size;
 
267
        free(ftp->cache);    /* free the cache */
 
268
        ftp->cache = NULL;   /* clear the pointer */
 
269
        ftp->cache_size = 0; /* zero the size just in case */
 
270
      }
 
271
      else {
 
272
        int res = Curl_read(conn, sockfd, ptr,
 
273
                            BUFSIZE-nread, &gotbytes);
 
274
        if(res < 0)
 
275
          /* EWOULDBLOCK */
 
276
          continue; /* go looping again */
 
277
 
 
278
        if(CURLE_OK != res)
 
279
          keepon = FALSE;
 
280
      }
 
281
 
 
282
      if(!keepon)
 
283
        ;
 
284
      else if(gotbytes <= 0) {
 
285
        keepon = FALSE;
 
286
        error = SELECT_ERROR;
 
287
        failf(data, "Connection aborted");
 
288
      }
 
289
      else {
 
290
        /* we got a whole chunk of data, which can be anything from one
 
291
         * byte to a set of lines and possible just a piece of the last
 
292
         * line */
 
293
        int i;
 
294
 
 
295
        nread += gotbytes;
 
296
        for(i = 0; i < gotbytes; ptr++, i++) {
 
297
          perline++;
 
298
          if(*ptr=='\n') {
 
299
            /* a newline is CRLF in ftp-talk, so the CR is ignored as
 
300
               the line isn't really terminated until the LF comes */
 
301
            CURLcode result;
 
302
 
 
303
            /* output debug output if that is requested */
 
304
            if(data->set.verbose) {
 
305
              fputs("< ", data->set.err);
 
306
              fwrite(line_start, perline, 1, data->set.err);
 
307
              /* no need to output LF here, it is part of the data */
 
308
            }
 
309
 
 
310
            /*
 
311
             * We pass all response-lines to the callback function registered
 
312
             * for "headers". The response lines can be seen as a kind of
 
313
             * headers.
 
314
             */
 
315
            result = Curl_client_write(data, CLIENTWRITE_HEADER,
 
316
                                       line_start, perline);
 
317
            if(result)
 
318
              return -SELECT_CALLBACK;
 
319
                                       
 
320
#define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
 
321
                        isdigit((int)line[2]) && (' ' == line[3]))
 
322
 
 
323
            if(perline>3 && lastline(line_start)) {
 
324
              /* This is the end of the last line, copy the last
 
325
               * line to the start of the buffer and zero terminate,
 
326
               * for old times sake (and krb4)! */
 
327
              char *meow;
 
328
              int n;
 
329
              for(meow=line_start, n=0; meow<ptr; meow++, n++)
 
330
                buf[n] = *meow;
 
331
              *meow=0; /* zero terminate */
 
332
              keepon=FALSE;
 
333
              line_start = ptr+1; /* advance pointer */
 
334
              i++; /* skip this before getting out */
 
335
              break;
 
336
            }
 
337
            perline=0; /* line starts over here */
 
338
            line_start = ptr+1;
 
339
          }
 
340
        }
 
341
        if(!keepon && (i != gotbytes)) {
 
342
          /* We found the end of the response lines, but we didn't parse the
 
343
             full chunk of data we have read from the server. We therefore
 
344
             need to store the rest of the data to be checked on the next
 
345
             invoke as it may actually contain another end of response
 
346
             already!  Cleverly figured out by Eric Lavigne in December
 
347
             2001. */
 
348
          ftp->cache_size = gotbytes - i;
 
349
          ftp->cache = (char *)malloc(ftp->cache_size);
 
350
          if(ftp->cache)
 
351
            memcpy(ftp->cache, line_start, ftp->cache_size);
 
352
          else
 
353
            return -SELECT_MEMORY; /**BANG**/
 
354
        }
 
355
      } /* there was data */
 
356
    } /* if(no error) */
 
357
  } /* while there's buffer left and loop is requested */
 
358
 
 
359
  if(!error)
 
360
    code = atoi(buf);
 
361
 
 
362
#ifdef KRB4
 
363
  /* handle the security-oriented responses 6xx ***/
 
364
  /* FIXME: some errorchecking perhaps... ***/
 
365
  switch(code) {
 
366
  case 631:
 
367
    Curl_sec_read_msg(conn, buf, prot_safe);
 
368
    break;
 
369
  case 632:
 
370
    Curl_sec_read_msg(conn, buf, prot_private);
 
371
    break;
 
372
  case 633:
 
373
    Curl_sec_read_msg(conn, buf, prot_confidential);
 
374
    break;
 
375
  default:
 
376
    /* normal ftp stuff we pass through! */
 
377
    break;
 
378
  }
 
379
#endif
 
380
 
 
381
  if(error)
 
382
    return -error;
 
383
 
 
384
  if(ftpcode)
 
385
    *ftpcode=code; /* return the initial number like this */
 
386
 
 
387
  return nread; /* total amount of bytes read */
 
388
}
 
389
 
 
390
#ifndef ENABLE_IPV6
 
391
/*
 
392
 * This function is only used by code that works on IPv4. When we add proper
 
393
 * support for that functionality with IPv6, this function can go in again.
 
394
 */
 
395
/* -- who are we? -- */
 
396
static char *getmyhost(char *buf, int buf_size)
 
397
{
 
398
#if defined(HAVE_GETHOSTNAME)
 
399
  gethostname(buf, buf_size);
 
400
#elif defined(HAVE_UNAME)
 
401
  struct utsname ugnm;
 
402
  strncpy(buf, uname(&ugnm) < 0 ? "localhost" : ugnm.nodename, buf_size - 1);
 
403
  buf[buf_size - 1] = '\0';
 
404
#else
 
405
  /* We have no means of finding the local host name! */
 
406
  strncpy(buf, "localhost", buf_size);
 
407
  buf[buf_size - 1] = '\0';
 
408
#endif
 
409
  return buf;
 
410
}
 
411
 
 
412
#endif /* ipv4-only function */
 
413
 
 
414
 
 
415
/* ftp_connect() should do everything that is to be considered a part
 
416
   of the connection phase. */
 
417
CURLcode Curl_ftp_connect(struct connectdata *conn)
 
418
{
 
419
  /* this is FTP and no proxy */
 
420
  int nread;
 
421
  struct SessionHandle *data=conn->data;
 
422
  char *buf = data->state.buffer; /* this is our buffer */
 
423
  struct FTP *ftp;
 
424
  CURLcode result;
 
425
  int ftpcode;
 
426
 
 
427
  ftp = (struct FTP *)malloc(sizeof(struct FTP));
 
428
  if(!ftp)
 
429
    return CURLE_OUT_OF_MEMORY;
 
430
 
 
431
  memset(ftp, 0, sizeof(struct FTP));
 
432
  conn->proto.ftp = ftp;
 
433
 
 
434
  /* We always support persistant connections on ftp */
 
435
  conn->bits.close = FALSE;
 
436
 
 
437
  /* get some initial data into the ftp struct */
 
438
  ftp->bytecountp = &conn->bytecount;
 
439
 
 
440
  /* no need to duplicate them, the data struct won't change */
 
441
  ftp->user = data->state.user;
 
442
  ftp->passwd = data->state.passwd;
 
443
 
 
444
  if (data->set.tunnel_thru_httpproxy) {
 
445
    /* We want "seamless" FTP operations through HTTP proxy tunnel */
 
446
    result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket,
 
447
                                         conn->hostname, conn->remote_port);
 
448
    if(CURLE_OK != result)
 
449
      return result;
 
450
  }
 
451
 
 
452
  if(conn->protocol & PROT_FTPS) {
 
453
    /* FTPS is simply ftp with SSL for the control channel */
 
454
    /* now, perform the SSL initialization for this socket */
 
455
    result = Curl_SSLConnect(conn);
 
456
    if(result)
 
457
      return result;
 
458
  }
 
459
 
 
460
 
 
461
  /* The first thing we do is wait for the "220*" line: */
 
462
  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
463
  if(nread < 0)
 
464
    return CURLE_OPERATION_TIMEOUTED;
 
465
 
 
466
  if(ftpcode != 220) {
 
467
    failf(data, "This doesn't seem like a nice ftp-server response");
 
468
    return CURLE_FTP_WEIRD_SERVER_REPLY;
 
469
  }
 
470
 
 
471
#ifdef KRB4
 
472
  /* if not anonymous login, try a secure login */
 
473
  if(data->set.krb4) {
 
474
 
 
475
    /* request data protection level (default is 'clear') */
 
476
    Curl_sec_request_prot(conn, "private");
 
477
 
 
478
    /* We set private first as default, in case the line below fails to
 
479
       set a valid level */
 
480
    Curl_sec_request_prot(conn, data->set.krb4_level);
 
481
 
 
482
    if(Curl_sec_login(conn) != 0)
 
483
      infof(data, "Logging in with password in cleartext!\n");
 
484
    else
 
485
      infof(data, "Authentication successful\n");
 
486
  }
 
487
#endif
 
488
  
 
489
  /* send USER */
 
490
  FTPSENDF(conn, "USER %s", ftp->user);
 
491
 
 
492
  /* wait for feedback */
 
493
  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
494
  if(nread < 0)
 
495
    return CURLE_OPERATION_TIMEOUTED;
 
496
 
 
497
  if(ftpcode == 530) {
 
498
    /* 530 User ... access denied
 
499
       (the server denies to log the specified user) */
 
500
    failf(data, "Access denied: %s", &buf[4]);
 
501
    return CURLE_FTP_ACCESS_DENIED;
 
502
  }
 
503
  else if(ftpcode == 331) {
 
504
    /* 331 Password required for ...
 
505
       (the server requires to send the user's password too) */
 
506
    FTPSENDF(conn, "PASS %s", ftp->passwd);
 
507
    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
508
    if(nread < 0)
 
509
      return CURLE_OPERATION_TIMEOUTED;
 
510
 
 
511
    if(ftpcode == 530) {
 
512
      /* 530 Login incorrect.
 
513
         (the username and/or the password are incorrect) */
 
514
      failf(data, "the username and/or the password are incorrect");
 
515
      return CURLE_FTP_USER_PASSWORD_INCORRECT;
 
516
    }
 
517
    else if(ftpcode == 230) {
 
518
      /* 230 User ... logged in.
 
519
         (user successfully logged in) */
 
520
        
 
521
      infof(data, "We have successfully logged in\n");
 
522
    }
 
523
    else {
 
524
      failf(data, "Odd return code after PASS");
 
525
      return CURLE_FTP_WEIRD_PASS_REPLY;
 
526
    }
 
527
  }
 
528
  else if(buf[0] == '2') {
 
529
    /* 230 User ... logged in.
 
530
       (the user logged in without password) */
 
531
    infof(data, "We have successfully logged in\n");
 
532
#ifdef KRB4
 
533
        /* we are logged in (with Kerberos)
 
534
         * now set the requested protection level
 
535
         */
 
536
    if(conn->sec_complete)
 
537
      Curl_sec_set_protection_level(conn);
 
538
 
 
539
    /* we may need to issue a KAUTH here to have access to the files
 
540
     * do it if user supplied a password
 
541
     */
 
542
    if(data->state.passwd && *data->state.passwd)
 
543
      Curl_krb_kauth(conn);
 
544
#endif
 
545
  }
 
546
  else {
 
547
    failf(data, "Odd return code after USER");
 
548
    return CURLE_FTP_WEIRD_USER_REPLY;
 
549
  }
 
550
 
 
551
  /* send PWD to discover our entry point */
 
552
  FTPSENDF(conn, "PWD", NULL);
 
553
 
 
554
  /* wait for feedback */
 
555
  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
556
  if(nread < 0)
 
557
    return CURLE_OPERATION_TIMEOUTED;
 
558
 
 
559
  if(ftpcode == 257) {
 
560
    char *dir = (char *)malloc(nread+1);
 
561
    char *store=dir;
 
562
    char *ptr=&buf[4]; /* start on the first letter */
 
563
    
 
564
    /* Reply format is like
 
565
       257<space>"<directory-name>"<space><commentary> and the RFC959 says
 
566
 
 
567
       The directory name can contain any character; embedded double-quotes
 
568
       should be escaped by double-quotes (the "quote-doubling" convention).
 
569
    */
 
570
    if('\"' == *ptr) {
 
571
      /* it started good */
 
572
      ptr++;
 
573
      while(ptr && *ptr) {
 
574
        if('\"' == *ptr) {
 
575
          if('\"' == ptr[1]) {
 
576
            /* "quote-doubling" */
 
577
            *store = ptr[1];
 
578
            ptr++;
 
579
          }
 
580
          else {
 
581
            /* end of path */
 
582
            *store = '\0'; /* zero terminate */
 
583
            break; /* get out of this loop */
 
584
          }
 
585
        }
 
586
        else
 
587
          *store = *ptr;
 
588
        store++;
 
589
        ptr++;
 
590
      }
 
591
      ftp->entrypath =dir; /* remember this */
 
592
      infof(data, "Entry path is '%s'\n", ftp->entrypath);
 
593
    }
 
594
    else {
 
595
      /* couldn't get the path */
 
596
    }
 
597
 
 
598
  }
 
599
  else {
 
600
    /* We couldn't read the PWD response! */
 
601
  }
 
602
 
 
603
  return CURLE_OK;
 
604
}
 
605
 
 
606
/***********************************************************************
 
607
 *
 
608
 * Curl_ftp_done()
 
609
 *
 
610
 * The DONE function. This does what needs to be done after a single DO has
 
611
 * performed.
 
612
 *
 
613
 * Input argument is already checked for validity.
 
614
 */
 
615
CURLcode Curl_ftp_done(struct connectdata *conn)
 
616
{
 
617
  struct SessionHandle *data = conn->data;
 
618
  struct FTP *ftp = conn->proto.ftp;
 
619
  ssize_t nread;
 
620
  char *buf = data->state.buffer; /* this is our buffer */
 
621
  int ftpcode;
 
622
  CURLcode result=CURLE_OK;
 
623
 
 
624
  if(data->set.upload) {
 
625
    if((-1 != data->set.infilesize) && (data->set.infilesize != *ftp->bytecountp)) {
 
626
      failf(data, "Wrote only partial file (%d out of %d bytes)",
 
627
            *ftp->bytecountp, data->set.infilesize);
 
628
      return CURLE_PARTIAL_FILE;
 
629
    }
 
630
  }
 
631
  else {
 
632
    if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
 
633
       (conn->maxdownload != *ftp->bytecountp)) {
 
634
      failf(data, "Received only partial file: %d bytes", *ftp->bytecountp);
 
635
      return CURLE_PARTIAL_FILE;
 
636
    }
 
637
    else if(!conn->bits.resume_done &&
 
638
            !data->set.no_body &&
 
639
            (0 == *ftp->bytecountp)) {
 
640
      /* We consider this an error, but there's no true FTP error received
 
641
         why we need to continue to "read out" the server response too.
 
642
         We don't want to leave a "waiting" server reply if we'll get told
 
643
         to make a second request on this same connection! */
 
644
      failf(data, "No data was received!");
 
645
      result = CURLE_FTP_COULDNT_RETR_FILE;
 
646
    }
 
647
  }
 
648
 
 
649
#ifdef KRB4
 
650
  Curl_sec_fflush_fd(conn, conn->secondarysocket);
 
651
#endif
 
652
  /* shut down the socket to inform the server we're done */
 
653
  sclose(conn->secondarysocket);
 
654
  conn->secondarysocket = -1;
 
655
 
 
656
  if(!data->set.no_body && !conn->bits.resume_done) {  
 
657
    /* now let's see what the server says about the transfer we
 
658
       just performed: */
 
659
    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
660
    if(nread < 0)
 
661
      return CURLE_OPERATION_TIMEOUTED;
 
662
 
 
663
    /* 226 Transfer complete, 250 Requested file action okay, completed. */
 
664
    if((ftpcode != 226) && (ftpcode != 250)) {
 
665
      failf(data, "server did not report OK, got %d", ftpcode);
 
666
      return CURLE_FTP_WRITE_ERROR;
 
667
    }
 
668
  }
 
669
 
 
670
  conn->bits.resume_done = FALSE; /* clean this for next connection */
 
671
 
 
672
  /* Send any post-transfer QUOTE strings? */
 
673
  if(!result && data->set.postquote)
 
674
    result = ftp_sendquote(conn, data->set.postquote);
 
675
 
 
676
  return result;
 
677
}
 
678
 
 
679
/***********************************************************************
 
680
 *
 
681
 * ftp_sendquote()
 
682
 *
 
683
 * Where a 'quote' means a list of custom commands to send to the server.
 
684
 * The quote list is passed as an argument.
 
685
 */
 
686
 
 
687
static 
 
688
CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
 
689
{
 
690
  struct curl_slist *item;
 
691
  ssize_t nread;
 
692
  int ftpcode;
 
693
  CURLcode result;
 
694
 
 
695
  item = quote;
 
696
  while (item) {
 
697
    if (item->data) {
 
698
      FTPSENDF(conn, "%s", item->data);
 
699
 
 
700
      nread = Curl_GetFTPResponse(conn->data->state.buffer, conn, &ftpcode);
 
701
      if (nread < 0)
 
702
        return CURLE_OPERATION_TIMEOUTED;
 
703
 
 
704
      if (ftpcode >= 400) {
 
705
        failf(conn->data, "QUOT string not accepted: %s", item->data);
 
706
        return CURLE_FTP_QUOTE_ERROR;
 
707
      }
 
708
    }
 
709
 
 
710
    item = item->next;
 
711
  }
 
712
 
 
713
  return CURLE_OK;
 
714
}
 
715
 
 
716
/***********************************************************************
 
717
 *
 
718
 * ftp_cwd()
 
719
 *
 
720
 * Send 'CWD' to the remote server to Change Working Directory.
 
721
 * It is the ftp version of the unix 'cd' command.
 
722
 */
 
723
static 
 
724
CURLcode ftp_cwd(struct connectdata *conn, char *path)
 
725
{
 
726
  ssize_t nread;
 
727
  int     ftpcode;
 
728
  CURLcode result;
 
729
  
 
730
  FTPSENDF(conn, "CWD %s", path);
 
731
  nread = Curl_GetFTPResponse(
 
732
                              conn->data->state.buffer, conn, &ftpcode);
 
733
  if (nread < 0)
 
734
    return CURLE_OPERATION_TIMEOUTED;
 
735
 
 
736
  if (ftpcode != 250) {
 
737
    failf(conn->data, "Couldn't cd to %s", path);
 
738
    return CURLE_FTP_ACCESS_DENIED;
 
739
  }
 
740
 
 
741
  return CURLE_OK;
 
742
}
 
743
 
 
744
/***********************************************************************
 
745
 *
 
746
 * ftp_getfiletime()
 
747
 *
 
748
 * Get the timestamp of the given file.
 
749
 */
 
750
static
 
751
CURLcode ftp_getfiletime(struct connectdata *conn, char *file)
 
752
{
 
753
  CURLcode result=CURLE_OK;
 
754
  int ftpcode; /* for ftp status */
 
755
  ssize_t nread;
 
756
  char *buf = conn->data->state.buffer;
 
757
 
 
758
  /* we have requested to get the modified-time of the file, this is yet
 
759
     again a grey area as the MDTM is not kosher RFC959 */
 
760
  FTPSENDF(conn, "MDTM %s", file);
 
761
 
 
762
  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
763
  if(nread < 0)
 
764
    return CURLE_OPERATION_TIMEOUTED;
 
765
 
 
766
  if(ftpcode == 213) {
 
767
    /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
 
768
       last .sss part is optional and means fractions of a second */
 
769
    int year, month, day, hour, minute, second;
 
770
    if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
 
771
                   &year, &month, &day, &hour, &minute, &second)) {
 
772
      /* we have a time, reformat it */
 
773
      time_t secs=time(NULL);
 
774
      sprintf(buf, "%04d%02d%02d %02d:%02d:%02d",
 
775
              year, month, day, hour, minute, second);
 
776
      /* now, convert this into a time() value: */
 
777
      conn->data->info.filetime = curl_getdate(buf, &secs);
 
778
    }
 
779
    else {
 
780
      infof(conn->data, "unsupported MDTM reply format\n");
 
781
    }
 
782
  }
 
783
  return  result;
 
784
}
 
785
 
 
786
/***********************************************************************
 
787
 *
 
788
 * ftp_transfertype()
 
789
 *
 
790
 * Set transfer type. We only deal with ASCII or BINARY so this function
 
791
 * sets one of them.
 
792
 */
 
793
static CURLcode ftp_transfertype(struct connectdata *conn,
 
794
                                  bool ascii)
 
795
{
 
796
  struct SessionHandle *data = conn->data;
 
797
  int ftpcode;
 
798
  ssize_t nread;
 
799
  char *buf=data->state.buffer;
 
800
  CURLcode result;
 
801
 
 
802
  FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
 
803
 
 
804
  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
805
  if(nread < 0)
 
806
    return CURLE_OPERATION_TIMEOUTED;
 
807
  
 
808
  if(ftpcode != 200) {
 
809
    failf(data, "Couldn't set %s mode",
 
810
          ascii?"ASCII":"binary");
 
811
    return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
 
812
  }
 
813
 
 
814
  return CURLE_OK;
 
815
}
 
816
 
 
817
/***********************************************************************
 
818
 *
 
819
 * ftp_getsize()
 
820
 *
 
821
 * Returns the file size (in bytes) of the given remote file.
 
822
 */
 
823
 
 
824
static
 
825
CURLcode ftp_getsize(struct connectdata *conn, char *file,
 
826
                      ssize_t *size)
 
827
{
 
828
  struct SessionHandle *data = conn->data;
 
829
  int ftpcode;
 
830
  ssize_t nread;
 
831
  char *buf=data->state.buffer;
 
832
  CURLcode result;
 
833
 
 
834
  FTPSENDF(conn, "SIZE %s", file);
 
835
  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
836
  if(nread < 0)
 
837
    return CURLE_OPERATION_TIMEOUTED;
 
838
 
 
839
  if(ftpcode == 213) {
 
840
    /* get the size from the ascii string: */
 
841
    *size = atoi(buf+4);
 
842
  }
 
843
  else
 
844
    return CURLE_FTP_COULDNT_GET_SIZE;
 
845
 
 
846
  return CURLE_OK;
 
847
}
 
848
 
 
849
/***************************************************************************
 
850
 *
 
851
 * ftp_pasv_verbose()
 
852
 *
 
853
 * This function only outputs some informationals about this second connection
 
854
 * when we've issued a PASV command before and thus we have connected to a
 
855
 * possibly new IP address.
 
856
 *
 
857
 */
 
858
static void
 
859
ftp_pasv_verbose(struct connectdata *conn,
 
860
                 Curl_ipconnect *addr,
 
861
                 char *newhost, /* ascii version */
 
862
                 int port)
 
863
{
 
864
#ifndef ENABLE_IPV6
 
865
  /*****************************************************************
 
866
   *
 
867
   * IPv4-only code section
 
868
   */
 
869
 
 
870
  struct in_addr in;
 
871
  struct hostent * answer;
 
872
 
 
873
#ifdef HAVE_INET_NTOA_R
 
874
  char ntoa_buf[64];
 
875
#endif
 
876
  /* The array size trick below is to make this a large chunk of memory
 
877
     suitably 8-byte aligned on 64-bit platforms. This was thoughtfully
 
878
     suggested by Philip Gladstone. */
 
879
  long bigbuf[9000 / sizeof(long)];
 
880
 
 
881
#if defined(HAVE_INET_ADDR)
 
882
  in_addr_t address;
 
883
# if defined(HAVE_GETHOSTBYADDR_R)
 
884
  int h_errnop;
 
885
# endif
 
886
  char *hostent_buf = (char *)bigbuf; /* get a char * to the buffer */
 
887
 
 
888
  address = inet_addr(newhost);
 
889
# ifdef HAVE_GETHOSTBYADDR_R
 
890
 
 
891
#  ifdef HAVE_GETHOSTBYADDR_R_5
 
892
  /* AIX, Digital Unix (OSF1, Tru64) style:
 
893
     extern int gethostbyaddr_r(char *addr, size_t len, int type,
 
894
     struct hostent *htent, struct hostent_data *ht_data); */
 
895
 
 
896
  /* Fred Noz helped me try this out, now it at least compiles! */
 
897
 
 
898
  /* Bjorn Reese (November 28 2001):
 
899
     The Tru64 man page on gethostbyaddr_r() says that
 
900
     the hostent struct must be filled with zeroes before the call to
 
901
     gethostbyaddr_r(). */
 
902
 
 
903
  memset(hostent_buf, 0, sizeof(struct hostent));
 
904
 
 
905
  if(gethostbyaddr_r((char *) &address,
 
906
                     sizeof(address), AF_INET,
 
907
                     (struct hostent *)hostent_buf,
 
908
                     hostent_buf + sizeof(*answer)))
 
909
    answer=NULL;
 
910
                           
 
911
#  endif
 
912
#  ifdef HAVE_GETHOSTBYADDR_R_7
 
913
  /* Solaris and IRIX */
 
914
  answer = gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
 
915
                           (struct hostent *)bigbuf,
 
916
                           hostent_buf + sizeof(*answer),
 
917
                           sizeof(hostent_buf) - sizeof(*answer),
 
918
                           &h_errnop);
 
919
#  endif
 
920
#  ifdef HAVE_GETHOSTBYADDR_R_8
 
921
  /* Linux style */
 
922
  if(gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
 
923
                     (struct hostent *)hostent_buf,
 
924
                     hostent_buf + sizeof(*answer),
 
925
                     sizeof(hostent_buf) - sizeof(*answer),
 
926
                     &answer,
 
927
                     &h_errnop))
 
928
    answer=NULL; /* error */
 
929
#  endif
 
930
        
 
931
# else
 
932
  answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
 
933
# endif
 
934
#else
 
935
  answer = NULL;
 
936
#endif
 
937
  (void) memcpy(&in.s_addr, addr, sizeof (Curl_ipconnect));
 
938
  infof(conn->data, "Connecting to %s (%s) port %u\n",
 
939
        answer?answer->h_name:newhost,
 
940
#if defined(HAVE_INET_NTOA_R)
 
941
        inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)),
 
942
#else
 
943
        inet_ntoa(in),
 
944
#endif
 
945
        port);
 
946
 
 
947
#else
 
948
  /*****************************************************************
 
949
   *
 
950
   * IPv6-only code section
 
951
   */
 
952
  char hbuf[NI_MAXHOST]; /* ~1KB */
 
953
  char nbuf[NI_MAXHOST]; /* ~1KB */
 
954
  char sbuf[NI_MAXSERV]; /* around 32 */
 
955
#ifdef NI_WITHSCOPEID
 
956
  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
 
957
#else
 
958
  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
 
959
#endif
 
960
  port = 0; /* unused, prevent warning */
 
961
  if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
 
962
                  nbuf, sizeof(nbuf), sbuf, sizeof(sbuf), niflags)) {
 
963
    snprintf(nbuf, sizeof(nbuf), "?");
 
964
    snprintf(sbuf, sizeof(sbuf), "?");
 
965
  }
 
966
        
 
967
  if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
 
968
                  hbuf, sizeof(hbuf), NULL, 0, 0)) {
 
969
    infof(conn->data, "Connecting to %s (%s) port %s\n", nbuf, newhost, sbuf);
 
970
  }
 
971
  else {
 
972
    infof(conn->data, "Connecting to %s (%s) port %s\n", hbuf, nbuf, sbuf);
 
973
  }
 
974
#endif
 
975
}
 
976
 
 
977
/***********************************************************************
 
978
 *
 
979
 * ftp_use_port()
 
980
 *
 
981
 * Send the proper PORT command. PORT is the ftp client's way of telling the
 
982
 * server that *WE* open a port that we listen on an awaits the server to
 
983
 * connect to. This is the opposite of PASV.
 
984
 */
 
985
 
 
986
static
 
987
CURLcode ftp_use_port(struct connectdata *conn)
 
988
{
 
989
  struct SessionHandle *data=conn->data;
 
990
  int portsock=-1;
 
991
  ssize_t nread;
 
992
  char *buf = data->state.buffer; /* this is our buffer */
 
993
  int ftpcode; /* receive FTP response codes in this */
 
994
  CURLcode result;
 
995
 
 
996
#ifdef ENABLE_IPV6
 
997
  /******************************************************************
 
998
   *
 
999
   * Here's a piece of IPv6-specific code coming up
 
1000
   *
 
1001
   */
 
1002
 
 
1003
  struct addrinfo hints, *res, *ai;
 
1004
  struct sockaddr_storage ss;
 
1005
  socklen_t sslen;
 
1006
  char hbuf[NI_MAXHOST];
 
1007
 
 
1008
  struct sockaddr *sa=(struct sockaddr *)&ss;
 
1009
#ifdef NI_WITHSCOPEID
 
1010
  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
 
1011
#else
 
1012
  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
 
1013
#endif
 
1014
  unsigned char *ap;
 
1015
  unsigned char *pp;
 
1016
  int alen, plen;
 
1017
  char portmsgbuf[4096], tmp[4096];
 
1018
 
 
1019
  const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
 
1020
  char **modep;
 
1021
 
 
1022
  /*
 
1023
   * we should use Curl_if2ip?  given pickiness of recent ftpd,
 
1024
   * I believe we should use the same address as the control connection.
 
1025
   */
 
1026
  sslen = sizeof(ss);
 
1027
  if (getsockname(conn->firstsocket, (struct sockaddr *)&ss, &sslen) < 0)
 
1028
    return CURLE_FTP_PORT_FAILED;
 
1029
  
 
1030
  if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
 
1031
                  niflags))
 
1032
    return CURLE_FTP_PORT_FAILED;
 
1033
 
 
1034
  memset(&hints, 0, sizeof(hints));
 
1035
  hints.ai_family = sa->sa_family;
 
1036
  /*hints.ai_family = ss.ss_family;
 
1037
    this way can be used if sockaddr_storage is properly defined, as glibc 
 
1038
    2.1.X doesn't do*/
 
1039
  hints.ai_socktype = SOCK_STREAM;
 
1040
  hints.ai_flags = AI_PASSIVE;
 
1041
 
 
1042
  if (getaddrinfo(hbuf, (char *)"0", &hints, &res))
 
1043
    return CURLE_FTP_PORT_FAILED;
 
1044
  
 
1045
  portsock = -1;
 
1046
  for (ai = res; ai; ai = ai->ai_next) {
 
1047
    portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
 
1048
    if (portsock < 0)
 
1049
      continue;
 
1050
 
 
1051
    if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
 
1052
      sclose(portsock);
 
1053
      portsock = -1;
 
1054
      continue;
 
1055
    }
 
1056
      
 
1057
    if (listen(portsock, 1) < 0) {
 
1058
      sclose(portsock);
 
1059
      portsock = -1;
 
1060
      continue;
 
1061
    }
 
1062
    
 
1063
    break;
 
1064
  }
 
1065
  freeaddrinfo(res);
 
1066
  if (portsock < 0) {
 
1067
    failf(data, strerror(errno));
 
1068
    return CURLE_FTP_PORT_FAILED;
 
1069
  }
 
1070
 
 
1071
  sslen = sizeof(ss);
 
1072
  if (getsockname(portsock, sa, &sslen) < 0) {
 
1073
    failf(data, strerror(errno));
 
1074
    return CURLE_FTP_PORT_FAILED;
 
1075
  }
 
1076
 
 
1077
  for (modep = (char **)mode; modep && *modep; modep++) {
 
1078
    int lprtaf, eprtaf;
 
1079
    
 
1080
    switch (sa->sa_family) {
 
1081
    case AF_INET:
 
1082
      ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
 
1083
      alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
 
1084
      pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
 
1085
      plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
 
1086
      lprtaf = 4;
 
1087
      eprtaf = 1;
 
1088
      break;
 
1089
    case AF_INET6:
 
1090
      ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
 
1091
      alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
 
1092
      pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
 
1093
      plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
 
1094
      lprtaf = 6;
 
1095
      eprtaf = 2;
 
1096
      break;
 
1097
    default:
 
1098
      ap = pp = NULL;
 
1099
      lprtaf = eprtaf = -1;
 
1100
      break;
 
1101
    }
 
1102
 
 
1103
    if (strcmp(*modep, "EPRT") == 0) {
 
1104
      if (eprtaf < 0)
 
1105
        continue;
 
1106
      if (getnameinfo((struct sockaddr *)&ss, sslen,
 
1107
                      portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags))
 
1108
        continue;
 
1109
 
 
1110
      /* do not transmit IPv6 scope identifier to the wire */
 
1111
      if (sa->sa_family == AF_INET6) {
 
1112
        char *q = strchr(portmsgbuf, '%');
 
1113
          if (q)
 
1114
            *q = '\0';
 
1115
      }
 
1116
 
 
1117
      result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", *modep, eprtaf,
 
1118
                             portmsgbuf, tmp);
 
1119
      if(result)
 
1120
        return result;
 
1121
    } else if (strcmp(*modep, "LPRT") == 0 ||
 
1122
               strcmp(*modep, "PORT") == 0) {
 
1123
      int i;
 
1124
      
 
1125
      if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0)
 
1126
        continue;
 
1127
      if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET)
 
1128
        continue;
 
1129
 
 
1130
      portmsgbuf[0] = '\0';
 
1131
      if (strcmp(*modep, "LPRT") == 0) {
 
1132
        snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
 
1133
        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
 
1134
            sizeof(portmsgbuf)) {
 
1135
          continue;
 
1136
        }
 
1137
      }
 
1138
 
 
1139
      for (i = 0; i < alen; i++) {
 
1140
        if (portmsgbuf[0])
 
1141
          snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
 
1142
        else
 
1143
          snprintf(tmp, sizeof(tmp), "%u", ap[i]);
 
1144
        
 
1145
        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
 
1146
            sizeof(portmsgbuf)) {
 
1147
          continue;
 
1148
        }
 
1149
      }
 
1150
      
 
1151
      if (strcmp(*modep, "LPRT") == 0) {
 
1152
        snprintf(tmp, sizeof(tmp), ",%d", plen);
 
1153
        
 
1154
        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
 
1155
          continue;
 
1156
      }
 
1157
 
 
1158
      for (i = 0; i < plen; i++) {
 
1159
        snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
 
1160
        
 
1161
        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
 
1162
            sizeof(portmsgbuf)) {
 
1163
          continue;
 
1164
        }
 
1165
      }
 
1166
      
 
1167
      result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf);
 
1168
      if(result)
 
1169
        return result;
 
1170
    }
 
1171
    
 
1172
    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
1173
    if(nread < 0)
 
1174
      return CURLE_OPERATION_TIMEOUTED;
 
1175
    
 
1176
    if (ftpcode != 200) {
 
1177
      failf(data, "Server does not grok %s", *modep);
 
1178
      continue;
 
1179
    }
 
1180
    else
 
1181
      break;
 
1182
  }
 
1183
  
 
1184
  if (!*modep) {
 
1185
    sclose(portsock);
 
1186
    return CURLE_FTP_PORT_FAILED;
 
1187
  }
 
1188
  /* we set the secondary socket variable to this for now, it
 
1189
     is only so that the cleanup function will close it in case
 
1190
     we fail before the true secondary stuff is made */
 
1191
  conn->secondarysocket = portsock;
 
1192
  
 
1193
#else
 
1194
  /******************************************************************
 
1195
   *
 
1196
   * Here's a piece of IPv4-specific code coming up
 
1197
   *
 
1198
   */
 
1199
  struct sockaddr_in sa;
 
1200
  struct hostent *h=NULL;
 
1201
  char *hostdataptr=NULL;
 
1202
  unsigned short porttouse;
 
1203
  char myhost[256] = "";
 
1204
 
 
1205
  if(data->set.ftpport) {
 
1206
    if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
 
1207
      h = Curl_resolv(data, myhost, 0, &hostdataptr);
 
1208
    }
 
1209
    else {
 
1210
      int len = strlen(data->set.ftpport);
 
1211
      if(len>1)
 
1212
        h = Curl_resolv(data, data->set.ftpport, 0, &hostdataptr);
 
1213
      if(h)
 
1214
        strcpy(myhost, data->set.ftpport); /* buffer overflow risk */
 
1215
    }
 
1216
  }
 
1217
  if(! *myhost) {
 
1218
    char *tmp_host = getmyhost(myhost, sizeof(myhost));
 
1219
    h=Curl_resolv(data, tmp_host, 0, &hostdataptr);
 
1220
  }
 
1221
  infof(data, "We connect from %s\n", myhost);
 
1222
  
 
1223
  if ( h ) {
 
1224
    if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) {
 
1225
      int size;
 
1226
      
 
1227
      /* we set the secondary socket variable to this for now, it
 
1228
         is only so that the cleanup function will close it in case
 
1229
         we fail before the true secondary stuff is made */
 
1230
      conn->secondarysocket = portsock;
 
1231
 
 
1232
      memset((char *)&sa, 0, sizeof(sa));
 
1233
      memcpy((char *)&sa.sin_addr,
 
1234
             h->h_addr,
 
1235
             h->h_length);
 
1236
      sa.sin_family = AF_INET;
 
1237
      sa.sin_addr.s_addr = INADDR_ANY;
 
1238
      sa.sin_port = 0;
 
1239
      size = sizeof(sa);
 
1240
      
 
1241
      if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
 
1242
        /* we succeeded to bind */
 
1243
        struct sockaddr_in add;
 
1244
        socklen_t socksize = sizeof(add);
 
1245
 
 
1246
        if(getsockname(portsock, (struct sockaddr *) &add,
 
1247
                       &socksize)<0) {
 
1248
          failf(data, "getsockname() failed");
 
1249
          return CURLE_FTP_PORT_FAILED;
 
1250
        }
 
1251
        porttouse = ntohs(add.sin_port);
 
1252
        
 
1253
        if ( listen(portsock, 1) < 0 ) {
 
1254
          failf(data, "listen(2) failed on socket");
 
1255
          free(hostdataptr);
 
1256
          return CURLE_FTP_PORT_FAILED;
 
1257
        }
 
1258
      }
 
1259
      else {
 
1260
        failf(data, "bind(2) failed on socket");
 
1261
        free(hostdataptr);
 
1262
        return CURLE_FTP_PORT_FAILED;
 
1263
      }
 
1264
    }
 
1265
    else {
 
1266
      failf(data, "socket(2) failed (%s)");
 
1267
      free(hostdataptr);
 
1268
      return CURLE_FTP_PORT_FAILED;
 
1269
    }
 
1270
  }
 
1271
  else {
 
1272
    failf(data, "could't find my own IP address (%s)", myhost);
 
1273
    return CURLE_FTP_PORT_FAILED;
 
1274
  }
 
1275
  {
 
1276
#ifdef HAVE_INET_NTOA_R
 
1277
    char ntoa_buf[64];
 
1278
#endif
 
1279
    struct in_addr in;
 
1280
    unsigned short ip[5];
 
1281
    (void) memcpy(&in.s_addr, *h->h_addr_list, sizeof (in.s_addr));
 
1282
#ifdef HAVE_INET_NTOA_R
 
1283
    /* ignore the return code from inet_ntoa_r() as it is int or
 
1284
       char * depending on system */
 
1285
    inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf));
 
1286
    sscanf( ntoa_buf, "%hu.%hu.%hu.%hu",
 
1287
            &ip[0], &ip[1], &ip[2], &ip[3]);
 
1288
#else
 
1289
    sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu",
 
1290
            &ip[0], &ip[1], &ip[2], &ip[3]);
 
1291
#endif
 
1292
    result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
 
1293
                         ip[0], ip[1], ip[2], ip[3],
 
1294
                         porttouse >> 8,
 
1295
                         porttouse & 255);
 
1296
    if(result)
 
1297
      return result;
 
1298
  }
 
1299
 
 
1300
  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
1301
  if(nread < 0)
 
1302
    return CURLE_OPERATION_TIMEOUTED;
 
1303
 
 
1304
  if(ftpcode != 200) {
 
1305
    failf(data, "Server does not grok PORT, try without it!");
 
1306
    return CURLE_FTP_PORT_FAILED;
 
1307
  }
 
1308
#endif /* end of ipv4-specific code */
 
1309
 
 
1310
  return CURLE_OK;
 
1311
}
 
1312
 
 
1313
/***********************************************************************
 
1314
 *
 
1315
 * ftp_use_pasv()
 
1316
 *
 
1317
 * Send the PASV command. PASV is the ftp client's way of asking the server to
 
1318
 * open a second port that we can connect to (for the data transfer). This is
 
1319
 * the opposite of PORT.
 
1320
 */
 
1321
 
 
1322
static
 
1323
CURLcode ftp_use_pasv(struct connectdata *conn)
 
1324
{
 
1325
  struct SessionHandle *data = conn->data;
 
1326
  ssize_t nread;
 
1327
  char *buf = data->state.buffer; /* this is our buffer */
 
1328
  int ftpcode; /* receive FTP response codes in this */
 
1329
  CURLcode result;
 
1330
  Curl_addrinfo *addr=NULL;
 
1331
  Curl_ipconnect *conninfo;
 
1332
 
 
1333
  /*
 
1334
    Here's the excecutive summary on what to do:
 
1335
 
 
1336
    PASV is RFC959, expect:
 
1337
    227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
 
1338
 
 
1339
    LPSV is RFC1639, expect:
 
1340
    228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
 
1341
 
 
1342
    EPSV is RFC2428, expect:
 
1343
    229 Entering Extended Passive Mode (|||port|)
 
1344
 
 
1345
  */
 
1346
 
 
1347
#if 1
 
1348
  const char *mode[] = { "EPSV", "PASV", NULL };
 
1349
  int results[] = { 229, 227, 0 };
 
1350
#else
 
1351
#if 0
 
1352
  char *mode[] = { "EPSV", "LPSV", "PASV", NULL };
 
1353
  int results[] = { 229, 228, 227, 0 };
 
1354
#else
 
1355
  const char *mode[] = { "PASV", NULL };
 
1356
  int results[] = { 227, 0 };
 
1357
#endif
 
1358
#endif
 
1359
  int modeoff;
 
1360
  unsigned short connectport; /* the local port connect() should use! */
 
1361
  unsigned short newport; /* remote port, not necessary the local one */
 
1362
  char *hostdataptr=NULL;
 
1363
  
 
1364
  /* newhost must be able to hold a full IP-style address in ASCII, which
 
1365
     in the IPv6 case means 5*8-1 = 39 letters */
 
1366
  char newhost[48];
 
1367
  char *newhostp=NULL;
 
1368
  
 
1369
  for (modeoff = (data->set.ftp_use_epsv?0:1);
 
1370
       mode[modeoff]; modeoff++) {
 
1371
    result = Curl_ftpsendf(conn, mode[modeoff]);
 
1372
    if(result)
 
1373
      return result;
 
1374
    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
1375
    if(nread < 0)
 
1376
      return CURLE_OPERATION_TIMEOUTED;
 
1377
    if (ftpcode == results[modeoff])
 
1378
      break;
 
1379
  }
 
1380
 
 
1381
  if (!mode[modeoff]) {
 
1382
    failf(data, "Odd return code after PASV");
 
1383
    return CURLE_FTP_WEIRD_PASV_REPLY;
 
1384
  }
 
1385
  else if (227 == results[modeoff]) {
 
1386
    int ip[4];
 
1387
    int port[2];
 
1388
    char *str=buf;
 
1389
 
 
1390
    /*
 
1391
     * New 227-parser June 3rd 1999.
 
1392
     * It now scans for a sequence of six comma-separated numbers and
 
1393
     * will take them as IP+port indicators.
 
1394
     *
 
1395
     * Found reply-strings include:
 
1396
     * "227 Entering Passive Mode (127,0,0,1,4,51)"
 
1397
     * "227 Data transfer will passively listen to 127,0,0,1,4,51"
 
1398
     * "227 Entering passive mode. 127,0,0,1,4,51"
 
1399
     */
 
1400
      
 
1401
    while(*str) {
 
1402
      if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
 
1403
                      &ip[0], &ip[1], &ip[2], &ip[3],
 
1404
                      &port[0], &port[1]))
 
1405
        break;
 
1406
      str++;
 
1407
    }
 
1408
 
 
1409
    if(!*str) {
 
1410
      failf(data, "Couldn't interpret this 227-reply: %s", buf);
 
1411
      return CURLE_FTP_WEIRD_227_FORMAT;
 
1412
    }
 
1413
 
 
1414
    sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
 
1415
    newhostp = newhost;
 
1416
    newport = (port[0]<<8) + port[1];
 
1417
  }
 
1418
#if 1
 
1419
  else if (229 == results[modeoff]) {
 
1420
    char *ptr = strchr(buf, '(');
 
1421
    if(ptr) {
 
1422
      unsigned int num;
 
1423
      char separator[4];
 
1424
      ptr++;
 
1425
      if(5  == sscanf(ptr, "%c%c%c%u%c",
 
1426
                      &separator[0],
 
1427
                      &separator[1],
 
1428
                      &separator[2],
 
1429
                      &num,
 
1430
                      &separator[3])) {
 
1431
        /* the four separators should be identical */
 
1432
        newport = num;
 
1433
 
 
1434
        /* we should use the same host we already are connected to */
 
1435
        newhostp = conn->name;
 
1436
      }                      
 
1437
      else
 
1438
        ptr=NULL;
 
1439
    }
 
1440
    if(!ptr) {
 
1441
      failf(data, "Weirdly formatted EPSV reply");
 
1442
      return CURLE_FTP_WEIRD_PASV_REPLY;
 
1443
    }
 
1444
  }
 
1445
#endif
 
1446
  else
 
1447
    return CURLE_FTP_CANT_RECONNECT;
 
1448
 
 
1449
  if(data->change.proxy) {
 
1450
    /*
 
1451
     * This is a tunnel through a http proxy and we need to connect to the
 
1452
     * proxy again here. We already have the name info for it since the
 
1453
     * previous lookup.
 
1454
     */
 
1455
    addr = conn->hostaddr;
 
1456
    connectport =
 
1457
      (unsigned short)conn->port; /* we connect to the proxy's port */
 
1458
  }
 
1459
  else {
 
1460
    /* normal, direct, ftp connection */
 
1461
    addr = Curl_resolv(data, newhostp, newport, &hostdataptr);
 
1462
    if(!addr) {
 
1463
      failf(data, "Can't resolve new host %s", newhost);
 
1464
      return CURLE_FTP_CANT_GET_HOST;
 
1465
    }
 
1466
    connectport = newport; /* we connect to the remote port */
 
1467
  }
 
1468
    
 
1469
  result = Curl_connecthost(conn,
 
1470
                            addr,
 
1471
                            connectport,
 
1472
                            &conn->secondarysocket,
 
1473
                            &conninfo);
 
1474
  
 
1475
  if((CURLE_OK == result) &&       
 
1476
     data->set.verbose)
 
1477
    /* this just dumps information about this second connection */
 
1478
    ftp_pasv_verbose(conn, conninfo, newhost, connectport);
 
1479
  
 
1480
  if(CURLE_OK != result)
 
1481
    return result;
 
1482
 
 
1483
  if (data->set.tunnel_thru_httpproxy) {
 
1484
    /* We want "seamless" FTP operations through HTTP proxy tunnel */
 
1485
    result = Curl_ConnectHTTPProxyTunnel(conn, conn->secondarysocket,
 
1486
                                         newhost, newport);
 
1487
    if(CURLE_OK != result)
 
1488
      return result;
 
1489
  }
 
1490
 
 
1491
  return CURLE_OK;
 
1492
}
 
1493
 
 
1494
/***********************************************************************
 
1495
 *
 
1496
 * ftp_perform()
 
1497
 *
 
1498
 * This is the actual DO function for FTP. Get a file/directory according to
 
1499
 * the options previously setup.
 
1500
 */
 
1501
 
 
1502
static
 
1503
CURLcode ftp_perform(struct connectdata *conn)
 
1504
{
 
1505
  /* this is FTP and no proxy */
 
1506
  ssize_t nread;
 
1507
  CURLcode result;
 
1508
  struct SessionHandle *data=conn->data;
 
1509
  char *buf = data->state.buffer; /* this is our buffer */
 
1510
 
 
1511
  /* the ftp struct is already inited in ftp_connect() */
 
1512
  struct FTP *ftp = conn->proto.ftp;
 
1513
 
 
1514
  long *bytecountp = ftp->bytecountp;
 
1515
  int ftpcode; /* for ftp status */
 
1516
 
 
1517
  /* Send any QUOTE strings? */
 
1518
  if(data->set.quote) {
 
1519
    if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
 
1520
      return result;
 
1521
  }
 
1522
    
 
1523
  /* This is a re-used connection. Since we change directory to where the
 
1524
     transfer is taking place, we must now get back to the original dir
 
1525
     where we ended up after login: */
 
1526
  if (conn->bits.reuse) {
 
1527
    if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK)
 
1528
      return result;
 
1529
  }
 
1530
 
 
1531
  /* change directory first! */
 
1532
  if(ftp->dir && ftp->dir[0]) {
 
1533
    if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK)
 
1534
        return result;
 
1535
  }
 
1536
 
 
1537
  /* Requested time of file? */
 
1538
  if(data->set.get_filetime && ftp->file) {
 
1539
    result = ftp_getfiletime(conn, ftp->file);
 
1540
    if(result)
 
1541
      return result;
 
1542
  }
 
1543
 
 
1544
  /* If we have selected NOBODY and HEADER, it means that we only want file
 
1545
     information. Which in FTP can't be much more than the file size and
 
1546
     date. */
 
1547
  if(data->set.no_body && data->set.include_header) {
 
1548
    /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
 
1549
       may not support it! It is however the only way we have to get a file's
 
1550
       size! */
 
1551
    ssize_t filesize;
 
1552
 
 
1553
    /* Some servers return different sizes for different modes, and thus we
 
1554
       must set the proper type before we check the size */
 
1555
    result = ftp_transfertype(conn, data->set.ftp_ascii);
 
1556
    if(result)
 
1557
      return result;
 
1558
 
 
1559
    /* failing to get size is not a serious error */
 
1560
    result = ftp_getsize(conn, ftp->file, &filesize);
 
1561
 
 
1562
    if(CURLE_OK == result) {
 
1563
      sprintf(buf, "Content-Length: %d\r\n", filesize);
 
1564
      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
 
1565
      if(result)
 
1566
        return result;
 
1567
    }
 
1568
 
 
1569
    /* If we asked for a time of the file and we actually got one as
 
1570
       well, we "emulate" a HTTP-style header in our output. */
 
1571
 
 
1572
#ifdef HAVE_STRFTIME
 
1573
    if(data->set.get_filetime && data->info.filetime) {
 
1574
      struct tm *tm;
 
1575
#ifdef HAVE_LOCALTIME_R
 
1576
      struct tm buffer;
 
1577
      tm = (struct tm *)localtime_r(&data->info.filetime, &buffer);
 
1578
#else
 
1579
      tm = localtime((unsigned long *)&data->info.filetime);
 
1580
#endif
 
1581
      /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
 
1582
      strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n",
 
1583
               tm);
 
1584
      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
 
1585
      if(result)
 
1586
        return result;
 
1587
    }
 
1588
#endif
 
1589
 
 
1590
    return CURLE_OK;
 
1591
  }
 
1592
 
 
1593
  if(data->set.no_body)
 
1594
    /* don't transfer the data */
 
1595
    ;
 
1596
  /* Get us a second connection up and connected */
 
1597
  else if(data->set.ftp_use_port) {
 
1598
    /* We have chosen to use the PORT command */
 
1599
    result = ftp_use_port(conn);
 
1600
    if(CURLE_OK == result)
 
1601
      /* we have the data connection ready */
 
1602
      infof(data, "Connected the data stream with PORT!\n");
 
1603
  }
 
1604
  else {
 
1605
    /* We have chosen (this is default) to use the PASV command */
 
1606
    result = ftp_use_pasv(conn);
 
1607
    if(CURLE_OK == result)
 
1608
      infof(data, "Connected the data stream with PASV!\n");
 
1609
  }
 
1610
  
 
1611
  if(result)
 
1612
    return result;
 
1613
 
 
1614
  if(data->set.upload) {
 
1615
 
 
1616
    /* Set type to binary (unless specified ASCII) */
 
1617
    result = ftp_transfertype(conn, data->set.ftp_ascii);
 
1618
    if(result)
 
1619
      return result;
 
1620
 
 
1621
    /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
 
1622
    if(data->set.prequote) {
 
1623
      if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
 
1624
        return result;
 
1625
    }
 
1626
 
 
1627
    if(conn->resume_from) {
 
1628
      /* we're about to continue the uploading of a file */
 
1629
      /* 1. get already existing file's size. We use the SIZE
 
1630
         command for this which may not exist in the server!
 
1631
         The SIZE command is not in RFC959. */
 
1632
 
 
1633
      /* 2. This used to set REST. But since we can do append, we
 
1634
         don't another ftp command. We just skip the source file
 
1635
         offset and then we APPEND the rest on the file instead */
 
1636
 
 
1637
      /* 3. pass file-size number of bytes in the source file */
 
1638
      /* 4. lower the infilesize counter */
 
1639
      /* => transfer as usual */
 
1640
 
 
1641
      if(conn->resume_from < 0 ) {
 
1642
        /* we could've got a specified offset from the command line,
 
1643
           but now we know we didn't */
 
1644
        ssize_t gottensize;
 
1645
 
 
1646
        if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) {
 
1647
          failf(data, "Couldn't get remote file size");
 
1648
          return CURLE_FTP_COULDNT_GET_SIZE;
 
1649
        }
 
1650
        conn->resume_from = gottensize;
 
1651
      }
 
1652
 
 
1653
      if(conn->resume_from) {
 
1654
        /* do we still game? */
 
1655
        int passed=0;
 
1656
        /* enable append instead */
 
1657
        data->set.ftp_append = 1;
 
1658
 
 
1659
        /* Now, let's read off the proper amount of bytes from the
 
1660
           input. If we knew it was a proper file we could've just
 
1661
           fseek()ed but we only have a stream here */
 
1662
        do {
 
1663
          int readthisamountnow = (conn->resume_from - passed);
 
1664
          int actuallyread;
 
1665
 
 
1666
          if(readthisamountnow > BUFSIZE)
 
1667
            readthisamountnow = BUFSIZE;
 
1668
 
 
1669
          actuallyread =
 
1670
            data->set.fread(data->state.buffer, 1, readthisamountnow,
 
1671
                            data->set.in);
 
1672
 
 
1673
          passed += actuallyread;
 
1674
          if(actuallyread != readthisamountnow) {
 
1675
            failf(data, "Could only read %d bytes from the input", passed);
 
1676
            return CURLE_FTP_COULDNT_USE_REST;
 
1677
          }
 
1678
        }
 
1679
        while(passed != conn->resume_from);
 
1680
 
 
1681
        /* now, decrease the size of the read */
 
1682
        if(data->set.infilesize>0) {
 
1683
          data->set.infilesize -= conn->resume_from;
 
1684
 
 
1685
          if(data->set.infilesize <= 0) {
 
1686
            infof(data, "File already completely uploaded\n");
 
1687
 
 
1688
            /* no data to transfer */
 
1689
            result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
 
1690
            
 
1691
            /* Set resume done so that we won't get any error in
 
1692
             * Curl_ftp_done() because we didn't transfer the amount of bytes
 
1693
             * that the local file file obviously is */
 
1694
            conn->bits.resume_done = TRUE; 
 
1695
 
 
1696
            return CURLE_OK;
 
1697
          }
 
1698
        }
 
1699
        /* we've passed, proceed as normal */
 
1700
      }
 
1701
    }
 
1702
 
 
1703
    /* Send everything on data->set.in to the socket */
 
1704
    if(data->set.ftp_append) {
 
1705
      /* we append onto the file instead of rewriting it */
 
1706
      FTPSENDF(conn, "APPE %s", ftp->file);
 
1707
    }
 
1708
    else {
 
1709
      FTPSENDF(conn, "STOR %s", ftp->file);
 
1710
    }
 
1711
 
 
1712
    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
1713
    if(nread < 0)
 
1714
      return CURLE_OPERATION_TIMEOUTED;
 
1715
 
 
1716
    if(ftpcode>=400) {
 
1717
      failf(data, "Failed FTP upload:%s", buf+3);
 
1718
      /* oops, we never close the sockets! */
 
1719
      return CURLE_FTP_COULDNT_STOR_FILE;
 
1720
    }
 
1721
 
 
1722
    if(data->set.ftp_use_port) {
 
1723
      /* PORT means we are now awaiting the server to connect to us. */
 
1724
      result = AllowServerConnect(data, conn, conn->secondarysocket);
 
1725
      if( result )
 
1726
        return result;
 
1727
    }
 
1728
 
 
1729
    *bytecountp=0;
 
1730
 
 
1731
    /* When we know we're uploading a specified file, we can get the file
 
1732
       size prior to the actual upload. */
 
1733
 
 
1734
    Curl_pgrsSetUploadSize(data, data->set.infilesize);
 
1735
 
 
1736
    result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
 
1737
                      conn->secondarysocket, bytecountp);
 
1738
    if(result)
 
1739
      return result;
 
1740
      
 
1741
  }
 
1742
  else if(!data->set.no_body) {
 
1743
    /* Retrieve file or directory */
 
1744
    bool dirlist=FALSE;
 
1745
    long downloadsize=-1;
 
1746
 
 
1747
    if(conn->bits.use_range && conn->range) {
 
1748
      long from, to;
 
1749
      int totalsize=-1;
 
1750
      char *ptr;
 
1751
      char *ptr2;
 
1752
 
 
1753
      from=strtol(conn->range, &ptr, 0);
 
1754
      while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
 
1755
        ptr++;
 
1756
      to=strtol(ptr, &ptr2, 0);
 
1757
      if(ptr == ptr2) {
 
1758
        /* we didn't get any digit */
 
1759
        to=-1;
 
1760
      }
 
1761
      if((-1 == to) && (from>=0)) {
 
1762
        /* X - */
 
1763
        conn->resume_from = from;
 
1764
        infof(data, "FTP RANGE %d to end of file\n", from);
 
1765
      }
 
1766
      else if(from < 0) {
 
1767
        /* -Y */
 
1768
        totalsize = -from;
 
1769
        conn->maxdownload = -from;
 
1770
        conn->resume_from = from;
 
1771
        infof(data, "FTP RANGE the last %d bytes\n", totalsize);
 
1772
      }
 
1773
      else {
 
1774
        /* X-Y */
 
1775
        totalsize = to-from;
 
1776
        conn->maxdownload = totalsize+1; /* include the last mentioned byte */
 
1777
        conn->resume_from = from;
 
1778
        infof(data, "FTP RANGE from %d getting %d bytes\n", from,
 
1779
              conn->maxdownload);
 
1780
      }
 
1781
      infof(data, "range-download from %d to %d, totally %d bytes\n",
 
1782
            from, to, totalsize);
 
1783
    }
 
1784
 
 
1785
    if((data->set.ftp_list_only) || !ftp->file) {
 
1786
      /* The specified path ends with a slash, and therefore we think this
 
1787
         is a directory that is requested, use LIST. But before that we
 
1788
         need to set ASCII transfer mode. */
 
1789
      dirlist = TRUE;
 
1790
 
 
1791
      /* Set type to ASCII */
 
1792
      result = ftp_transfertype(conn, TRUE /* ASCII enforced */);
 
1793
      if(result)
 
1794
        return result;
 
1795
 
 
1796
      /* if this output is to be machine-parsed, the NLST command will be
 
1797
         better used since the LIST command output is not specified or
 
1798
         standard in any way */
 
1799
 
 
1800
      FTPSENDF(conn, "%s",
 
1801
            data->set.customrequest?data->set.customrequest:
 
1802
            (data->set.ftp_list_only?"NLST":"LIST"));
 
1803
    }
 
1804
    else {
 
1805
      ssize_t foundsize;
 
1806
 
 
1807
      /* Set type to binary (unless specified ASCII) */
 
1808
      result = ftp_transfertype(conn, data->set.ftp_ascii);
 
1809
      if(result)
 
1810
        return result;
 
1811
 
 
1812
      /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
 
1813
      if(data->set.prequote) {
 
1814
        if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
 
1815
          return result;
 
1816
      }
 
1817
 
 
1818
      /* Attempt to get the size, it'll be useful in some cases: for resumed
 
1819
         downloads and when talking to servers that don't give away the size
 
1820
         in the RETR response line. */
 
1821
      result = ftp_getsize(conn, ftp->file, &foundsize);
 
1822
      if(CURLE_OK == result)
 
1823
        downloadsize = foundsize;
 
1824
 
 
1825
      if(conn->resume_from) {
 
1826
 
 
1827
        /* Daniel: (August 4, 1999)
 
1828
         *
 
1829
         * We start with trying to use the SIZE command to figure out the size
 
1830
         * of the file we're gonna get. If we can get the size, this is by far
 
1831
         * the best way to know if we're trying to resume beyond the EOF.
 
1832
         *
 
1833
         * Daniel, November 28, 2001. We *always* get the size on downloads
 
1834
         * now, so it is done before this even when not doing resumes. I saved
 
1835
         * the comment above for nostalgical reasons! ;-)
 
1836
         */
 
1837
        if(CURLE_OK != result) {
 
1838
          infof(data, "ftp server doesn't support SIZE\n");
 
1839
          /* We couldn't get the size and therefore we can't know if there
 
1840
             really is a part of the file left to get, although the server
 
1841
             will just close the connection when we start the connection so it
 
1842
             won't cause us any harm, just not make us exit as nicely. */
 
1843
        }
 
1844
        else {
 
1845
          /* We got a file size report, so we check that there actually is a
 
1846
             part of the file left to get, or else we go home.  */
 
1847
          if(conn->resume_from< 0) {
 
1848
            /* We're supposed to download the last abs(from) bytes */
 
1849
            if(foundsize < -conn->resume_from) {
 
1850
              failf(data, "Offset (%d) was beyond file size (%d)",
 
1851
                    conn->resume_from, foundsize);
 
1852
              return CURLE_FTP_BAD_DOWNLOAD_RESUME;
 
1853
            }
 
1854
            /* convert to size to download */
 
1855
            downloadsize = -conn->resume_from;
 
1856
            /* download from where? */
 
1857
            conn->resume_from = foundsize - downloadsize;
 
1858
          }
 
1859
          else {
 
1860
            if(foundsize < conn->resume_from) {
 
1861
              failf(data, "Offset (%d) was beyond file size (%d)",
 
1862
                    conn->resume_from, foundsize);
 
1863
              return CURLE_FTP_BAD_DOWNLOAD_RESUME;
 
1864
            }
 
1865
            /* Now store the number of bytes we are expected to download */
 
1866
            downloadsize = foundsize-conn->resume_from;
 
1867
          }
 
1868
        }
 
1869
 
 
1870
        if (downloadsize == 0) {
 
1871
          /* no data to transfer */
 
1872
          result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
 
1873
          infof(data, "File already completely downloaded\n");
 
1874
 
 
1875
          /* Set resume done so that we won't get any error in Curl_ftp_done()
 
1876
           * because we didn't transfer the amount of bytes that the remote
 
1877
           * file obviously is */
 
1878
          conn->bits.resume_done = TRUE; 
 
1879
 
 
1880
          return CURLE_OK;
 
1881
        }
 
1882
        
 
1883
        /* Set resume file transfer offset */
 
1884
        infof(data, "Instructs server to resume from offset %d\n",
 
1885
              conn->resume_from);
 
1886
 
 
1887
        FTPSENDF(conn, "REST %d", conn->resume_from);
 
1888
 
 
1889
        nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
1890
        if(nread < 0)
 
1891
          return CURLE_OPERATION_TIMEOUTED;
 
1892
 
 
1893
        if(ftpcode != 350) {
 
1894
          failf(data, "Couldn't use REST: %s", buf+4);
 
1895
          return CURLE_FTP_COULDNT_USE_REST;
 
1896
        }
 
1897
      }
 
1898
 
 
1899
      FTPSENDF(conn, "RETR %s", ftp->file);
 
1900
    }
 
1901
 
 
1902
    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
 
1903
    if(nread < 0)
 
1904
      return CURLE_OPERATION_TIMEOUTED;
 
1905
 
 
1906
    if((ftpcode == 150) || (ftpcode == 125)) {
 
1907
 
 
1908
      /*
 
1909
        A;
 
1910
        150 Opening BINARY mode data connection for /etc/passwd (2241
 
1911
        bytes).  (ok, the file is being transfered)
 
1912
        
 
1913
        B:
 
1914
        150 Opening ASCII mode data connection for /bin/ls 
 
1915
 
 
1916
        C:
 
1917
        150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
 
1918
 
 
1919
        D:
 
1920
        150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
 
1921
          
 
1922
        E:
 
1923
        125 Data connection already open; Transfer starting. */
 
1924
 
 
1925
      int size=-1; /* default unknown size */
 
1926
 
 
1927
      if(!dirlist &&
 
1928
         !data->set.ftp_ascii &&
 
1929
         (-1 == downloadsize)) {
 
1930
        /*
 
1931
         * It seems directory listings either don't show the size or very
 
1932
         * often uses size 0 anyway. ASCII transfers may very well turn out
 
1933
         * that the transfered amount of data is not the same as this line
 
1934
         * tells, why using this number in those cases only confuses us.
 
1935
         *
 
1936
         * Example D above makes this parsing a little tricky */
 
1937
        char *bytes;
 
1938
        bytes=strstr(buf, " bytes");
 
1939
        if(bytes--) {
 
1940
          int index=bytes-buf;
 
1941
          /* this is a hint there is size information in there! ;-) */
 
1942
          while(--index) {
 
1943
            /* scan for the parenthesis and break there */
 
1944
            if('(' == *bytes)
 
1945
              break;
 
1946
            /* if only skip digits, or else we're in deep trouble */
 
1947
            if(!isdigit((int)*bytes)) {
 
1948
              bytes=NULL;
 
1949
              break;
 
1950
            }
 
1951
            /* one more estep backwards */
 
1952
            bytes--;
 
1953
          }
 
1954
          /* only if we have nothing but digits: */
 
1955
          if(bytes++) {
 
1956
            /* get the number! */
 
1957
            size = atoi(bytes);
 
1958
          }
 
1959
            
 
1960
        }
 
1961
      }
 
1962
      else if(downloadsize > -1)
 
1963
        size = downloadsize;
 
1964
 
 
1965
      if(data->set.ftp_use_port) {
 
1966
        result = AllowServerConnect(data, conn, conn->secondarysocket);
 
1967
        if( result )
 
1968
          return result;
 
1969
      }
 
1970
 
 
1971
      infof(data, "Getting file with size: %d\n", size);
 
1972
 
 
1973
      /* FTP download: */
 
1974
      result=Curl_Transfer(conn, conn->secondarysocket, size, FALSE,
 
1975
                           bytecountp,
 
1976
                           -1, NULL); /* no upload here */
 
1977
      if(result)
 
1978
        return result;
 
1979
    }
 
1980
    else {
 
1981
      failf(data, "%s", buf+4);
 
1982
      return CURLE_FTP_COULDNT_RETR_FILE;
 
1983
    }
 
1984
        
 
1985
  }
 
1986
  /* end of transfer */
 
1987
 
 
1988
  return CURLE_OK;
 
1989
}
 
1990
 
 
1991
/***********************************************************************
 
1992
 *
 
1993
 * Curl_ftp()
 
1994
 *
 
1995
 * This function is registered as 'curl_do' function. It decodes the path
 
1996
 * parts etc as a wrapper to the actual DO function (ftp_perform).
 
1997
 *
 
1998
 * The input argument is already checked for validity.
 
1999
 */
 
2000
CURLcode Curl_ftp(struct connectdata *conn)
 
2001
{
 
2002
  CURLcode retcode;
 
2003
 
 
2004
  struct SessionHandle *data = conn->data;
 
2005
  struct FTP *ftp;
 
2006
  int dirlength=0; /* 0 forces strlen() */
 
2007
 
 
2008
  /* the ftp struct is already inited in ftp_connect() */
 
2009
  ftp = conn->proto.ftp;
 
2010
 
 
2011
  /* We split the path into dir and file parts *before* we URLdecode
 
2012
     it */
 
2013
  ftp->file = strrchr(conn->ppath, '/');
 
2014
  if(ftp->file) {
 
2015
    if(ftp->file != conn->ppath)
 
2016
      dirlength=ftp->file-conn->ppath; /* don't count the traling slash */
 
2017
 
 
2018
    ftp->file++; /* point to the first letter in the file name part or
 
2019
                    remain NULL */
 
2020
  }
 
2021
  else {
 
2022
    ftp->file = conn->ppath; /* there's only a file part */
 
2023
  }
 
2024
 
 
2025
  if(*ftp->file) {
 
2026
    ftp->file = curl_unescape(ftp->file, 0);
 
2027
    if(NULL == ftp->file) {
 
2028
      failf(data, "no memory");
 
2029
      return CURLE_OUT_OF_MEMORY;
 
2030
    }
 
2031
  }
 
2032
  else
 
2033
    ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
 
2034
                       pointer */
 
2035
 
 
2036
  ftp->urlpath = conn->ppath;
 
2037
  if(dirlength) {
 
2038
    ftp->dir = curl_unescape(ftp->urlpath, dirlength);
 
2039
    if(NULL == ftp->dir) {
 
2040
      if(ftp->file)
 
2041
        free(ftp->file);
 
2042
      failf(data, "no memory");
 
2043
      return CURLE_OUT_OF_MEMORY; /* failure */
 
2044
    }
 
2045
  }
 
2046
  else
 
2047
    ftp->dir = NULL;
 
2048
 
 
2049
  retcode = ftp_perform(conn);
 
2050
 
 
2051
  /* clean up here, success or error doesn't matter */
 
2052
  if(ftp->file)
 
2053
    free(ftp->file);
 
2054
  if(ftp->dir)
 
2055
    free(ftp->dir);
 
2056
 
 
2057
  ftp->file = ftp->dir = NULL; /* zero */
 
2058
 
 
2059
  return retcode;
 
2060
}
 
2061
 
 
2062
/***********************************************************************
 
2063
 *
 
2064
 * Curl_ftpsendf()
 
2065
 *
 
2066
 * Sends the formated string as a ftp command to a ftp server
 
2067
 *
 
2068
 * NOTE: we build the command in a fixed-length buffer, which sets length
 
2069
 * restrictions on the command!
 
2070
 */
 
2071
CURLcode Curl_ftpsendf(struct connectdata *conn,
 
2072
                       const char *fmt, ...)
 
2073
{
 
2074
  ssize_t bytes_written;
 
2075
  char s[256];
 
2076
  ssize_t write_len;
 
2077
  char *sptr=s;
 
2078
  CURLcode res = CURLE_OK;
 
2079
 
 
2080
  va_list ap;
 
2081
  va_start(ap, fmt);
 
2082
  vsnprintf(s, 250, fmt, ap);
 
2083
  va_end(ap);
 
2084
 
 
2085
  if(conn->data->set.verbose)
 
2086
    fprintf(conn->data->set.err, "> %s\n", s);
 
2087
 
 
2088
  strcat(s, "\r\n"); /* append a trailing CRLF */
 
2089
 
 
2090
  bytes_written=0;
 
2091
  write_len = strlen(s);
 
2092
 
 
2093
  do {
 
2094
    res = Curl_write(conn, conn->firstsocket, sptr, write_len,
 
2095
                     &bytes_written);
 
2096
 
 
2097
    if(CURLE_OK != res)
 
2098
      break;
 
2099
 
 
2100
    if(bytes_written != write_len) {
 
2101
      write_len -= bytes_written;
 
2102
      sptr += bytes_written;
 
2103
    }
 
2104
    else
 
2105
      break;
 
2106
  } while(1);
 
2107
 
 
2108
  return res;
 
2109
}
 
2110
 
 
2111
/***********************************************************************
 
2112
 *
 
2113
 * Curl_ftp_disconnect()
 
2114
 *
 
2115
 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
 
2116
 * resources
 
2117
 */
 
2118
CURLcode Curl_ftp_disconnect(struct connectdata *conn)
 
2119
{
 
2120
  struct FTP *ftp= conn->proto.ftp;
 
2121
 
 
2122
  /* The FTP session may or may not have been allocated/setup at this point! */
 
2123
  if(ftp) {
 
2124
    if(ftp->entrypath)
 
2125
      free(ftp->entrypath);
 
2126
    if(ftp->cache)
 
2127
      free(ftp->cache);
 
2128
  }
 
2129
  return CURLE_OK;
 
2130
}
 
2131
 
 
2132
/*
 
2133
 * local variables:
 
2134
 * eval: (load-file "../curl-mode.el")
 
2135
 * end:
 
2136
 * vim600: fdm=marker
 
2137
 * vim: et sw=2 ts=2 sts=2 tw=78
 
2138
 */