curl: -O crash on windows
[platform/upstream/curl.git] / src / main.c
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  ***************************************************************************/
22 #include "setup.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <ctype.h>
31 #include <errno.h>
32
33 #if defined(MSDOS) || defined(WIN32)
34 #  if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
35 #    include <libgen.h>
36 #  endif
37 #endif
38
39 #include <curl/curl.h>
40
41 #include "urlglob.h"
42 #include "writeout.h"
43 #include "getpass.h"
44 #include "homedir.h"
45 #include "curlutil.h"
46 #ifdef USE_MANUAL
47 #include "hugehelp.h"
48 #endif
49 #ifdef USE_ENVIRONMENT
50 #include "writeenv.h"
51 #endif
52 #include "rawstr.h"
53
54 #define CURLseparator   "--_curl_--"
55
56 #ifdef NETWARE
57 #ifdef __NOVELL_LIBC__
58 #include <screen.h>
59 #else
60 #include <nwconio.h>
61 #define mkdir mkdir_510
62 #endif
63 #endif
64
65 #include "version.h"
66
67 #ifdef HAVE_IO_H /* typical win32 habit */
68 #include <io.h>
69 #endif
70
71 #ifdef HAVE_UNISTD_H
72 #include <unistd.h>
73 #endif
74
75 #ifdef HAVE_FCNTL_H
76 #include <fcntl.h>
77 #endif
78
79 #ifdef HAVE_UTIME_H
80 #include <utime.h>
81 #else
82 #ifdef HAVE_SYS_UTIME_H
83 #include <sys/utime.h>
84 #endif
85
86 #endif /* HAVE_UTIME_H */
87
88 #ifdef HAVE_LIMITS_H
89 #include <limits.h>
90 #endif
91
92 #ifdef HAVE_SYS_POLL_H
93 #include <sys/poll.h>
94 #elif defined(HAVE_POLL_H)
95 #include <poll.h>
96 #endif
97
98 #ifdef HAVE_LOCALE_H
99 #include <locale.h> /* for setlocale() */
100 #endif
101
102 #define ENABLE_CURLX_PRINTF
103 /* make the curlx header define all printf() functions to use the curlx_*
104    versions instead */
105 #include "curlx.h" /* header from the libcurl directory */
106
107 #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
108 #include <iconv.h>
109 /* set default codesets for iconv */
110 #ifndef CURL_ICONV_CODESET_OF_NETWORK
111 #define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1"
112 #endif
113 #endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
114
115 #ifdef HAVE_NETINET_IN_H
116 #include <netinet/in.h> /* for IPPROTO_TCP */
117 #endif
118 #ifdef HAVE_NETINET_TCP_H
119 #include <netinet/tcp.h> /* for TCP_KEEPIDLE, TCP_KEEPINTVL */
120 #endif
121
122 #include "os-specific.h"
123
124 /* The last #include file should be: */
125 #ifdef CURLDEBUG
126 #ifndef CURLTOOLDEBUG
127 #define MEMDEBUG_NODEFINES
128 #endif
129 /* This is low-level hard-hacking memory leak tracking and similar. Using
130    the library level code from this client-side is ugly, but we do this
131    anyway for convenience. */
132 #include "memdebug.h"
133 #endif
134
135 #ifdef __VMS
136 static int vms_show = 0;
137 #endif
138
139 #if defined(NETWARE)
140 #define PRINT_LINES_PAUSE 23
141 #endif
142
143 #if defined(__SYMBIAN32__)
144 #define PRINT_LINES_PAUSE 16
145 #define pressanykey() getchar()
146 #endif
147
148 #define DEFAULT_MAXREDIRS  50L
149
150 #if defined(O_BINARY) && defined(HAVE_SETMODE)
151   #ifdef __HIGHC__
152     #define SET_BINMODE(file) _setmode(file,O_BINARY)
153   #else
154     #define SET_BINMODE(file) setmode(fileno(file),O_BINARY)
155   #endif
156 #else
157   #define SET_BINMODE(file)   ((void)0)
158 #endif
159
160 #ifndef O_BINARY
161 /* since O_BINARY as used in bitmasks, setting it to zero makes it usable in
162    source code but yet it doesn't ruin anything */
163 #define O_BINARY 0
164 #endif
165
166 #if defined(MSDOS) || defined(WIN32)
167
168 static const char *msdosify(const char *);
169 static char *rename_if_dos_device_name(char *);
170 static char *sanitize_dos_name(char *);
171
172 #ifndef S_ISCHR
173 #  ifdef S_IFCHR
174 #    define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
175 #  else
176 #    define S_ISCHR(m) (0) /* cannot tell if file is a device */
177 #  endif
178 #endif
179
180 #ifdef WIN32
181 #  define _use_lfn(f) (1)  /* long file names always available */
182 #elif !defined(__DJGPP__) || (__DJGPP__ < 2)  /* DJGPP 2.0 has _use_lfn() */
183 #  define _use_lfn(f) (0)  /* long file names never available */
184 #endif
185
186 #endif /* MSDOS || WIN32 */
187
188 #ifdef MSDOS
189 #define USE_WATT32
190 #include <dos.h>
191
192 #ifdef DJGPP
193 /* we want to glob our own argv[] */
194 char **__crt0_glob_function (char *arg)
195 {
196   (void)arg;
197   return (char**)0;
198 }
199 #endif /* __DJGPP__ */
200 #endif /* MSDOS */
201
202 #ifndef STDIN_FILENO
203 #define STDIN_FILENO  fileno(stdin)
204 #endif
205
206 #ifndef STDOUT_FILENO
207 #define STDOUT_FILENO  fileno(stdout)
208 #endif
209
210 #ifndef STDERR_FILENO
211 #define STDERR_FILENO  fileno(stderr)
212 #endif
213
214 #define CURL_PROGRESS_STATS 0 /* default progress display */
215 #define CURL_PROGRESS_BAR   1
216
217 typedef enum {
218   HTTPREQ_UNSPEC,
219   HTTPREQ_GET,
220   HTTPREQ_HEAD,
221   HTTPREQ_POST,
222   HTTPREQ_SIMPLEPOST,
223   HTTPREQ_CUSTOM,
224   HTTPREQ_LAST
225 } HttpReq;
226
227 /*
228  * Large file support (>2Gb) using WIN32 functions.
229  */
230
231 #ifdef USE_WIN32_LARGE_FILES
232 #  include <io.h>
233 #  include <sys/types.h>
234 #  include <sys/stat.h>
235 #  define lseek(fdes,offset,whence)  _lseeki64(fdes, offset, whence)
236 #  define fstat(fdes,stp)            _fstati64(fdes, stp)
237 #  define stat(fname,stp)            _stati64(fname, stp)
238 #  define struct_stat                struct _stati64
239 #  define LSEEK_ERROR                (__int64)-1
240 #endif
241
242 /*
243  * Small file support (<2Gb) using WIN32 functions.
244  */
245
246 #ifdef USE_WIN32_SMALL_FILES
247 #  include <io.h>
248 #  include <sys/types.h>
249 #  include <sys/stat.h>
250 #  define lseek(fdes,offset,whence)  _lseek(fdes, (long)offset, whence)
251 #  define fstat(fdes,stp)            _fstat(fdes, stp)
252 #  define stat(fname,stp)            _stat(fname, stp)
253 #  define struct_stat                struct _stat
254 #  define LSEEK_ERROR                (long)-1
255 #endif
256
257 #ifndef struct_stat
258 #  define struct_stat struct stat
259 #endif
260
261 #ifndef LSEEK_ERROR
262 #  define LSEEK_ERROR (off_t)-1
263 #endif
264
265 #ifdef WIN32
266 #  include <direct.h>
267 #  define mkdir(x,y) (mkdir)(x)
268 #  undef  PATH_MAX
269 #  define PATH_MAX MAX_PATH
270 #  ifndef __POCC__
271 #    define F_OK 0
272 #  endif
273 #endif
274
275 /*
276  * Default sizeof(off_t) in case it hasn't been defined in config file.
277  */
278
279 #ifndef SIZEOF_OFF_T
280 #  if defined(__VMS) && !defined(__VAX)
281 #    if defined(_LARGEFILE)
282 #      define SIZEOF_OFF_T 8
283 #    endif
284 #  elif defined(__OS400__) && defined(__ILEC400__)
285 #    if defined(_LARGE_FILES)
286 #      define SIZEOF_OFF_T 8
287 #    endif
288 #  elif defined(__MVS__) && defined(__IBMC__)
289 #    if defined(_LP64) || defined(_LARGE_FILES)
290 #      define SIZEOF_OFF_T 8
291 #    endif
292 #  elif defined(__370__) && defined(__IBMC__)
293 #    if defined(_LP64) || defined(_LARGE_FILES)
294 #      define SIZEOF_OFF_T 8
295 #    endif
296 #  elif defined(TPF)
297 #    define SIZEOF_OFF_T 8
298 #  endif
299 #  ifndef SIZEOF_OFF_T
300 #    define SIZEOF_OFF_T 4
301 #  endif
302 #endif
303
304 #ifdef CURL_DOES_CONVERSIONS
305 #ifdef HAVE_ICONV
306 iconv_t inbound_cd  = (iconv_t)-1;
307 iconv_t outbound_cd = (iconv_t)-1;
308
309 /*
310  * convert_to_network() is an internal function to convert
311  * from the host encoding to ASCII on non-ASCII platforms.
312  */
313 static CURLcode
314 convert_to_network(char *buffer, size_t length)
315 {
316   CURLcode rc;
317
318   /* translate from the host encoding to the network encoding */
319   char *input_ptr, *output_ptr;
320   size_t in_bytes, out_bytes;
321
322   /* open an iconv conversion descriptor if necessary */
323   if(outbound_cd == (iconv_t)-1) {
324     outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
325                              CURL_ICONV_CODESET_OF_HOST);
326     if(outbound_cd == (iconv_t)-1) {
327       return CURLE_CONV_FAILED;
328     }
329   }
330   /* call iconv */
331   input_ptr = output_ptr = buffer;
332   in_bytes = out_bytes = length;
333   rc = iconv(outbound_cd, &input_ptr,  &in_bytes,
334                           &output_ptr, &out_bytes);
335   if ((rc == -1) || (in_bytes != 0)) {
336     return CURLE_CONV_FAILED;
337   }
338
339   return CURLE_OK;
340 }
341
342 /*
343  * convert_from_network() is an internal function
344  * for performing ASCII conversions on non-ASCII platforms.
345  */
346 static CURLcode
347 convert_from_network(char *buffer, size_t length)
348 {
349   CURLcode rc;
350
351   /* translate from the network encoding to the host encoding */
352   char *input_ptr, *output_ptr;
353   size_t in_bytes, out_bytes;
354
355   /* open an iconv conversion descriptor if necessary */
356   if(inbound_cd == (iconv_t)-1) {
357     inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
358                             CURL_ICONV_CODESET_OF_NETWORK);
359     if(inbound_cd == (iconv_t)-1) {
360       return CURLE_CONV_FAILED;
361     }
362   }
363   /* call iconv */
364   input_ptr = output_ptr = buffer;
365   in_bytes = out_bytes = length;
366   rc = iconv(inbound_cd, &input_ptr,  &in_bytes,
367                          &output_ptr, &out_bytes);
368   if ((rc == -1) || (in_bytes != 0)) {
369     return CURLE_CONV_FAILED;
370   }
371
372   return CURLE_OK;
373 }
374 #endif /* HAVE_ICONV */
375
376 static
377 char convert_char(curl_infotype infotype, char this_char)
378 {
379 /* determine how this specific character should be displayed */
380   switch(infotype) {
381   case CURLINFO_DATA_IN:
382   case CURLINFO_DATA_OUT:
383   case CURLINFO_SSL_DATA_IN:
384   case CURLINFO_SSL_DATA_OUT:
385     /* data, treat as ASCII */
386     if ((this_char >= 0x20) && (this_char < 0x7f)) {
387       /* printable ASCII hex value: convert to host encoding */
388       convert_from_network(&this_char, 1);
389     }
390     else {
391       /* non-printable ASCII, use a replacement character */
392       return UNPRINTABLE_CHAR;
393     }
394     /* fall through to default */
395   default:
396     /* treat as host encoding */
397     if (ISPRINT(this_char)
398         &&  (this_char != '\t')
399         &&  (this_char != '\r')
400         &&  (this_char != '\n')) {
401       /* printable characters excluding tabs and line end characters */
402       return this_char;
403     }
404     break;
405   }
406   /* non-printable, use a replacement character  */
407   return UNPRINTABLE_CHAR;
408 }
409 #endif /* CURL_DOES_CONVERSIONS */
410
411 #ifdef WIN32
412
413 #ifdef __BORLANDC__
414    /* 64-bit lseek-like function unavailable */
415 #  define _lseeki64(hnd,ofs,whence) lseek(hnd,ofs,whence)
416 #endif
417
418 #ifdef __POCC__
419 #  if (__POCC__ < 450)
420      /* 64-bit lseek-like function unavailable */
421 #    define _lseeki64(hnd,ofs,whence) _lseek(hnd,ofs,whence)
422 #  else
423 #    define _lseeki64(hnd,ofs,whence) _lseek64(hnd,ofs,whence)
424 #  endif
425 #endif
426
427 #ifndef HAVE_FTRUNCATE
428 #define HAVE_FTRUNCATE 1
429 #endif
430
431 /*
432  * Truncate a file handle at a 64-bit position 'where'.
433  */
434
435 static int ftruncate64 (int fd, curl_off_t where)
436 {
437   if(_lseeki64(fd, where, SEEK_SET) < 0)
438     return -1;
439
440   if(!SetEndOfFile((HANDLE)_get_osfhandle(fd)))
441     return -1;
442
443   return 0;
444 }
445 #define ftruncate(fd,where) ftruncate64(fd,where)
446
447 #endif /* WIN32 */
448
449 typedef enum {
450   TRACE_NONE,  /* no trace/verbose output at all! */
451   TRACE_BIN,   /* tcpdump inspired look */
452   TRACE_ASCII, /* like *BIN but without the hex output */
453   TRACE_PLAIN  /* -v/--verbose type */
454 } trace;
455
456 struct OutStruct {
457   char *filename;
458   FILE *stream;
459   struct Configurable *config;
460   curl_off_t bytes; /* amount written so far */
461   curl_off_t init;  /* original size (non-zero when appending) */
462 };
463
464 struct Configurable {
465   CURL *easy; /* once we have one, we keep it here */
466   bool remote_time;
467   char *random_file;
468   char *egd_file;
469   char *useragent;
470   char *cookie;     /* single line with specified cookies */
471   char *cookiejar;  /* write to this file */
472   char *cookiefile; /* read from this file */
473   bool cookiesession; /* new session? */
474   bool encoding;    /* Accept-Encoding please */
475   long authtype;    /* auth bitmask */
476   bool use_resume;
477   bool resume_from_current;
478   bool disable_epsv;
479   bool disable_eprt;
480   bool ftp_pret;
481   curl_off_t resume_from;
482   char *postfields;
483   curl_off_t postfieldsize;
484   char *referer;
485   long timeout;
486   long connecttimeout;
487   long maxredirs;
488   curl_off_t max_filesize;
489   char *headerfile;
490   char *ftpport;
491   char *iface;
492   int localport;
493   int localportrange;
494   unsigned short porttouse;
495   char *range;
496   long low_speed_limit;
497   long low_speed_time;
498   bool showerror;
499   char *userpwd;
500   char *proxyuserpwd;
501   char *proxy;
502   int proxyver;     /* set to CURLPROXY_HTTP* define */
503   char *noproxy;
504   char *mail_from;
505   struct curl_slist *mail_rcpt;
506   bool proxytunnel;
507   bool ftp_append;         /* APPE on ftp */
508   bool mute;               /* shutup */
509   bool use_ascii;          /* select ascii or text transfer */
510   bool autoreferer;        /* automatically set referer */
511   bool failonerror;        /* fail on (HTTP) errors */
512   bool include_headers;    /* send headers to data output */
513   bool no_body;            /* don't get the body */
514   bool dirlistonly;        /* only get the FTP dir list */
515   bool followlocation;     /* follow http redirects */
516   bool unrestricted_auth;  /* Continue to send authentication (user+password)
517                               when following ocations, even when hostname
518                               changed */
519   bool netrc_opt;
520   bool netrc;
521   bool noprogress;
522   bool isatty;             /* updated internally only if the output is a tty */
523   struct getout *url_list; /* point to the first node */
524   struct getout *url_last; /* point to the last/current node */
525   struct getout *url_get;  /* point to the node to fill in URL */
526   struct getout *url_out;  /* point to the node to fill in outfile */
527   char *cipher_list;
528   char *cert;
529   char *cert_type;
530   char *cacert;
531   char *capath;
532   char *crlfile;
533   char *key;
534   char *key_type;
535   char *key_passwd;
536   char *pubkey;
537   char *hostpubmd5;
538   char *engine;
539   bool list_engines;
540   bool crlf;
541   char *customrequest;
542   char *krblevel;
543   char *trace_dump; /* file to dump the network trace to, or NULL */
544   FILE *trace_stream;
545   bool trace_fopened;
546   trace tracetype;
547   bool tracetime; /* include timestamp? */
548   long httpversion;
549   int progressmode;
550   bool nobuffer;
551   bool readbusy; /* set when reading input returns EAGAIN */
552   bool globoff;
553   bool use_httpget;
554   bool insecure_ok; /* set TRUE to allow insecure SSL connects */
555   bool create_dirs;
556   bool ftp_create_dirs;
557   bool ftp_skip_ip;
558   bool proxynegotiate;
559   bool proxyntlm;
560   bool proxydigest;
561   bool proxybasic;
562   bool proxyanyauth;
563   char *writeout; /* %-styled format string to output */
564   bool writeenv; /* write results to environment, if available */
565   FILE *errors; /* if stderr redirect is requested */
566   bool errors_fopened;
567   struct curl_slist *quote;
568   struct curl_slist *postquote;
569   struct curl_slist *prequote;
570   long ssl_version;
571   long ip_version;
572   curl_TimeCond timecond;
573   time_t condtime;
574   struct curl_slist *headers;
575   struct curl_httppost *httppost;
576   struct curl_httppost *last_post;
577   struct curl_slist *telnet_options;
578   HttpReq httpreq;
579
580   /* for bandwidth limiting features: */
581   curl_off_t sendpersecond; /* send to peer */
582   curl_off_t recvpersecond; /* receive from peer */
583
584   bool ftp_ssl;
585   bool ftp_ssl_reqd;
586   bool ftp_ssl_control;
587   bool ftp_ssl_ccc;
588   int ftp_ssl_ccc_mode;
589
590   char *socksproxy; /* set to server string */
591   int socksver;     /* set to CURLPROXY_SOCKS* define */
592   char *socks5_gssapi_service;  /* set service name for gssapi principal
593                                  * default rcmd */
594   int socks5_gssapi_nec ;  /* The NEC reference server does not protect
595                             * the encryption type exchange */
596
597   bool tcp_nodelay;
598   long req_retry;   /* number of retries */
599   long retry_delay; /* delay between retries (in seconds) */
600   long retry_maxtime; /* maximum time to keep retrying */
601
602   char *ftp_account; /* for ACCT */
603   char *ftp_alternative_to_user; /* send command if USER/PASS fails */
604   int ftp_filemethod;
605   long tftp_blksize; /* TFTP BLKSIZE option */
606   bool ignorecl; /* --ignore-content-length */
607   bool disable_sessionid;
608
609   char *libcurl; /* output libcurl code to this file name */
610   bool raw;
611   bool post301;
612   bool post302;
613   bool nokeepalive; /* for keepalive needs */
614   long alivetime;
615   bool content_disposition; /* use Content-disposition filename */
616
617   int default_node_flags; /* default flags to seach for each 'node', which is
618                              basically each given URL to transfer */
619   struct OutStruct *outs;
620 };
621
622 #define WARN_PREFIX "Warning: "
623 #define WARN_TEXTWIDTH (79 - (int)strlen(WARN_PREFIX))
624 /* produce this text message to the user unless mute was selected */
625 static void warnf(struct Configurable *config, const char *fmt, ...)
626 {
627   if(!config->mute) {
628     va_list ap;
629     int len;
630     char *ptr;
631     char print_buffer[256];
632
633     va_start(ap, fmt);
634     len = vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
635     va_end(ap);
636
637     ptr = print_buffer;
638     while(len > 0) {
639       fputs(WARN_PREFIX, config->errors);
640
641       if(len > (int)WARN_TEXTWIDTH) {
642         int cut = WARN_TEXTWIDTH-1;
643
644         while(!ISSPACE(ptr[cut]) && cut) {
645           cut--;
646         }
647         if(0 == cut)
648           /* not a single cutting position was found, just cut it at the
649              max text width then! */
650           cut = WARN_TEXTWIDTH-1;
651
652         (void)fwrite(ptr, cut + 1, 1, config->errors);
653         fputs("\n", config->errors);
654         ptr += cut+1; /* skip the space too */
655         len -= cut;
656       }
657       else {
658         fputs(ptr, config->errors);
659         len = 0;
660       }
661     }
662   }
663 }
664
665 /*
666  * This is the main global constructor for the app. Call this before
667  * _any_ libcurl usage. If this fails, *NO* libcurl functions may be
668  * used, or havoc may be the result.
669  */
670 static CURLcode main_init(void)
671 {
672 #ifdef DJGPP
673   /* stop stat() wasting time */
674   _djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
675 #endif
676
677   return curl_global_init(CURL_GLOBAL_DEFAULT);
678 }
679
680 /*
681  * This is the main global destructor for the app. Call this after
682  * _all_ libcurl usage is done.
683  */
684 static void main_free(void)
685 {
686   curl_global_cleanup();
687 #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
688   /* close iconv conversion descriptor */
689   if(inbound_cd != (iconv_t)-1)
690     iconv_close(inbound_cd);
691   if(outbound_cd != (iconv_t)-1)
692     iconv_close(outbound_cd);
693 #endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
694 }
695
696 static int SetHTTPrequest(struct Configurable *config,
697                           HttpReq req, HttpReq *store)
698 {
699   if((*store == HTTPREQ_UNSPEC) ||
700      (*store == req)) {
701     *store = req;
702     return 0;
703   }
704   warnf(config, "You can only select one HTTP request!\n");
705   return 1;
706 }
707
708 static void helpf(FILE *errors, const char *fmt, ...)
709 {
710   va_list ap;
711   if(fmt) {
712     va_start(ap, fmt);
713     fputs("curl: ", errors); /* prefix it */
714     vfprintf(errors, fmt, ap);
715     va_end(ap);
716   }
717   fprintf(errors, "curl: try 'curl --help' "
718 #ifdef USE_MANUAL
719           "or 'curl --manual' "
720 #endif
721           "for more information\n");
722 }
723
724 /*
725  * A chain of these nodes contain URL to get and where to put the URL's
726  * contents.
727  */
728 struct getout {
729   struct getout *next; /* next one */
730   char *url;     /* the URL we deal with */
731   char *outfile; /* where to store the output */
732   char *infile;  /* file to upload, if GETOUT_UPLOAD is set */
733   int flags;     /* options */
734 };
735 #define GETOUT_OUTFILE (1<<0)   /* set when outfile is deemed done */
736 #define GETOUT_URL     (1<<1)   /* set when URL is deemed done */
737 #define GETOUT_USEREMOTE (1<<2) /* use remote file name locally */
738 #define GETOUT_UPLOAD  (1<<3)   /* if set, -T has been used */
739 #define GETOUT_NOUPLOAD  (1<<4) /* if set, -T "" has been used */
740
741 static void help(void)
742 {
743   int i;
744   /* A few of these source lines are >80 columns wide, but that's only because
745      breaking the strings narrower makes this chunk look even worse!
746
747      Starting with 7.18.0, this list of command line options is sorted based
748      on the long option name. It is not done automatically, although a command
749      line like the following can help out:
750
751      curl --help | cut -c5- | grep "^-" | sort
752   */
753   static const char * const helptext[]={
754     "Usage: curl [options...] <url>",
755     "Options: (H) means HTTP/HTTPS only, (F) means FTP only",
756     "    --anyauth       Pick \"any\" authentication method (H)",
757     " -a/--append        Append to target file when uploading (F/SFTP)",
758     "    --basic         Use HTTP Basic Authentication (H)",
759     "    --cacert <file> CA certificate to verify peer against (SSL)",
760     "    --capath <directory> CA directory to verify peer against (SSL)",
761     " -E/--cert <cert[:passwd]> Client certificate file and password (SSL)",
762     "    --cert-type <type> Certificate file type (DER/PEM/ENG) (SSL)",
763     "    --ciphers <list> SSL ciphers to use (SSL)",
764     "    --compressed    Request compressed response (using deflate or gzip)",
765     " -K/--config <file> Specify which config file to read",
766     "    --connect-timeout <seconds> Maximum time allowed for connection",
767     " -C/--continue-at <offset> Resumed transfer offset",
768     " -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)",
769     " -c/--cookie-jar <file> Write cookies to this file after operation (H)",
770     "    --create-dirs   Create necessary local directory hierarchy",
771     "    --crlf          Convert LF to CRLF in upload",
772     "    --crlfile <file> Get a CRL list in PEM format from the given file",
773     " -d/--data <data>   HTTP POST data (H)",
774     "    --data-ascii <data>  HTTP POST ASCII data (H)",
775     "    --data-binary <data> HTTP POST binary data (H)",
776     "    --data-urlencode <name=data/name@filename> HTTP POST data url encoded (H)",
777     "    --digest        Use HTTP Digest Authentication (H)",
778     "    --disable-eprt  Inhibit using EPRT or LPRT (F)",
779     "    --disable-epsv  Inhibit using EPSV (F)",
780     " -D/--dump-header <file> Write the headers to this file",
781     "    --egd-file <file> EGD socket path for random data (SSL)",
782     "    --engine <eng>  Crypto engine to use (SSL). \"--engine list\" for list",
783 #ifdef USE_ENVIRONMENT
784     "    --environment   Write results to environment variables (RISC OS)",
785 #endif
786     " -f/--fail          Fail silently (no output at all) on HTTP errors (H)",
787     " -F/--form <name=content> Specify HTTP multipart POST data (H)",
788     "    --form-string <name=string> Specify HTTP multipart POST data (H)",
789     "    --ftp-account <data> Account data to send when requested by server (F)",
790     "    --ftp-alternative-to-user <cmd> String to replace \"USER [name]\" (F)",
791     "    --ftp-create-dirs Create the remote dirs if not present (F)",
792     "    --ftp-method [multicwd/nocwd/singlecwd] Control CWD usage (F)",
793     "    --ftp-pasv      Use PASV/EPSV instead of PORT (F)",
794     " -P/--ftp-port <address> Use PORT with address instead of PASV (F)",
795     "    --ftp-skip-pasv-ip Skip the IP address for PASV (F)\n"
796     "    --ftp-pret      Send PRET before PASV (for drftpd) (F)",
797     "    --ftp-ssl-ccc   Send CCC after authenticating (F)",
798     "    --ftp-ssl-ccc-mode [active/passive] Set CCC mode (F)",
799     "    --ftp-ssl-control Require SSL/TLS for ftp login, clear for transfer (F)",
800     " -G/--get           Send the -d data with a HTTP GET (H)",
801     " -g/--globoff       Disable URL sequences and ranges using {} and []",
802     " -H/--header <line> Custom header to pass to server (H)",
803     " -I/--head          Show document info only",
804     " -h/--help          This help text",
805     "    --hostpubmd5 <md5> Hex encoded MD5 string of the host public key. (SSH)",
806     " -0/--http1.0       Use HTTP 1.0 (H)",
807     "    --ignore-content-length  Ignore the HTTP Content-Length header",
808     " -i/--include       Include protocol headers in the output (H/F)",
809     " -k/--insecure      Allow connections to SSL sites without certs (H)",
810     "    --interface <interface> Specify network interface/address to use",
811     " -4/--ipv4          Resolve name to IPv4 address",
812     " -6/--ipv6          Resolve name to IPv6 address",
813     " -j/--junk-session-cookies Ignore session cookies read from file (H)",
814     "    --keepalive-time <seconds> Interval between keepalive probes",
815     "    --key <key>     Private key file name (SSL/SSH)",
816     "    --key-type <type> Private key file type (DER/PEM/ENG) (SSL)",
817     "    --krb <level>   Enable Kerberos with specified security level (F)",
818     "    --libcurl <file> Dump libcurl equivalent code of this command line",
819     "    --limit-rate <rate> Limit transfer speed to this rate",
820     " -J/--remote-header-name Use the header-provided filename (H)",
821     " -l/--list-only     List only names of an FTP directory (F)",
822     "    --local-port <num>[-num] Force use of these local port numbers",
823     " -L/--location      Follow Location: hints (H)",
824     "    --location-trusted Follow Location: and send auth to other hosts (H)",
825     " -M/--manual        Display the full manual",
826     "    --mail-from <from> Mail from this address",
827     "    --mail-rcpt <to> Mail to this receiver(s)",
828     "    --max-filesize <bytes> Maximum file size to download (H/F)",
829     "    --max-redirs <num> Maximum number of redirects allowed (H)",
830     " -m/--max-time <seconds> Maximum time allowed for the transfer",
831     "    --negotiate     Use HTTP Negotiate Authentication (H)",
832     " -n/--netrc         Must read .netrc for user name and password",
833     "    --netrc-optional Use either .netrc or URL; overrides -n",
834     " -N/--no-buffer     Disable buffering of the output stream",
835     "    --no-keepalive  Disable keepalive use on the connection",
836     "    --no-sessionid  Disable SSL session-ID reusing (SSL)",
837     "    --noproxy       Comma-separated list of hosts which do not use proxy",
838     "    --ntlm          Use HTTP NTLM authentication (H)",
839     " -o/--output <file> Write output to <file> instead of stdout",
840     "    --pass  <pass>  Pass phrase for the private key (SSL/SSH)",
841     "    --post301       Do not switch to GET after following a 301 redirect (H)",
842     "    --post302       Do not switch to GET after following a 302 redirect (H)",
843     " -#/--progress-bar  Display transfer progress as a progress bar",
844     " -x/--proxy <host[:port]> Use HTTP proxy on given port",
845     "    --proxy-anyauth Pick \"any\" proxy authentication method (H)",
846     "    --proxy-basic   Use Basic authentication on the proxy (H)",
847     "    --proxy-digest  Use Digest authentication on the proxy (H)",
848     "    --proxy-negotiate Use Negotiate authentication on the proxy (H)",
849     "    --proxy-ntlm    Use NTLM authentication on the proxy (H)",
850     " -U/--proxy-user <user[:password]> Set proxy user and password",
851     "    --proxy1.0 <host[:port]> Use HTTP/1.0 proxy on given port",
852     " -p/--proxytunnel   Operate through a HTTP proxy tunnel (using CONNECT)",
853     "    --pubkey <key>  Public key file name (SSH)",
854     " -Q/--quote <cmd>   Send command(s) to server before file transfer (F/SFTP)",
855     "    --random-file <file> File for reading random data from (SSL)",
856     " -r/--range <range> Retrieve only the bytes within a range",
857     "    --raw           Pass HTTP \"raw\", without any transfer decoding (H)",
858     " -e/--referer       Referer URL (H)",
859     " -O/--remote-name   Write output to a file named as the remote file",
860     "    --remote-name-all Use the remote file name for all URLs",
861     " -R/--remote-time   Set the remote file's time on the local output",
862     " -X/--request <command> Specify request command to use",
863     "    --retry <num>   Retry request <num> times if transient problems occur",
864     "    --retry-delay <seconds> When retrying, wait this many seconds between each",
865     "    --retry-max-time <seconds> Retry only within this period",
866     " -S/--show-error    Show error. With -s, make curl show errors when they occur",
867     " -s/--silent        Silent mode. Don't output anything",
868     "    --socks4 <host[:port]> SOCKS4 proxy on given host + port",
869     "    --socks4a <host[:port]> SOCKS4a proxy on given host + port",
870     "    --socks5 <host[:port]> SOCKS5 proxy on given host + port",
871     "    --socks5-hostname <host[:port]> SOCKS5 proxy, pass host name to proxy",
872 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
873     "    --socks5-gssapi-service <name> SOCKS5 proxy service name for gssapi",
874     "    --socks5-gssapi-nec  Compatibility with NEC SOCKS5 server",
875 #endif
876     " -Y/--speed-limit   Stop transfer if below speed-limit for 'speed-time' secs",
877     " -y/--speed-time    Time needed to trig speed-limit abort. Defaults to 30",
878     "    --ssl           Try SSL/TLS (FTP, IMAP, POP3, SMTP)",
879     "    --ssl-reqd      Require SSL/TLS (FTP, IMAP, POP3, SMTP)",
880     " -2/--sslv2         Use SSLv2 (SSL)",
881     " -3/--sslv3         Use SSLv3 (SSL)",
882     "    --stderr <file> Where to redirect stderr. - means stdout",
883     "    --tcp-nodelay   Use the TCP_NODELAY option",
884     " -t/--telnet-option <OPT=val> Set telnet option",
885     "    --tftp-blksize <value> Set TFTP BLKSIZE option (must be >512)",
886     " -z/--time-cond <time> Transfer based on a time condition",
887     " -1/--tlsv1         Use TLSv1 (SSL)",
888     "    --trace <file>  Write a debug trace to the given file",
889     "    --trace-ascii <file> Like --trace but without the hex output",
890     "    --trace-time    Add time stamps to trace/verbose output",
891     " -T/--upload-file <file> Transfer <file> to remote site",
892     "    --url <URL>     Set URL to work with",
893     " -B/--use-ascii     Use ASCII/text transfer",
894     " -u/--user <user[:password]> Set server user and password",
895     " -A/--user-agent <string> User-Agent to send to server (H)",
896     " -v/--verbose       Make the operation more talkative",
897     " -V/--version       Show version number and quit",
898
899 #ifdef USE_WATT32
900     "    --wdebug        Turn on Watt-32 debugging",
901 #endif
902     " -w/--write-out <format> What to output after completion",
903     " -q                 If used as the first parameter disables .curlrc",
904     NULL
905   };
906   for(i=0; helptext[i]; i++) {
907     puts(helptext[i]);
908 #ifdef PRINT_LINES_PAUSE
909     if (i && ((i % PRINT_LINES_PAUSE) == 0))
910       pressanykey();
911 #endif
912   }
913 }
914
915 struct LongShort {
916   const char *letter;
917   const char *lname;
918   bool extraparam;
919 };
920
921 /* global variable to hold info about libcurl */
922 static curl_version_info_data *curlinfo;
923
924 static int parseconfig(const char *filename,
925                        struct Configurable *config);
926 static char *my_get_line(FILE *fp);
927 static int create_dir_hierarchy(const char *outfile, FILE *errors);
928
929 static void GetStr(char **string,
930                    const char *value)
931 {
932   if(*string)
933     free(*string);
934   if(value)
935     *string = strdup(value);
936   else
937     *string = NULL;
938 }
939
940 static void clean_getout(struct Configurable *config)
941 {
942   struct getout *node=config->url_list;
943   struct getout *next;
944
945   while(node) {
946     next = node->next;
947     if(node->url)
948       free(node->url);
949     if(node->outfile)
950       free(node->outfile);
951     if(node->infile)
952       free(node->infile);
953     free(node);
954
955     node = next; /* GOTO next */
956   }
957 }
958
959 static struct getout *new_getout(struct Configurable *config)
960 {
961   struct getout *node =malloc(sizeof(struct getout));
962   struct getout *last= config->url_last;
963   if(node) {
964     /* clear the struct */
965     memset(node, 0, sizeof(struct getout));
966
967     /* append this new node last in the list */
968     if(last)
969       last->next = node;
970     else
971       config->url_list = node; /* first node */
972
973     /* move the last pointer */
974     config->url_last = node;
975
976     node->flags = config->default_node_flags;
977   }
978   return node;
979 }
980
981 /* Structure for storing the information needed to build a multiple files
982  * section
983 */
984 struct multi_files {
985   struct curl_forms   form;
986   struct multi_files *next;
987 };
988
989 /* Add a new list entry possibly with a type_name
990  */
991 static struct multi_files *
992 AddMultiFiles (const char *file_name,
993                const char *type_name,
994                const char *show_filename,
995                struct multi_files **multi_start,
996                struct multi_files **multi_current)
997 {
998   struct multi_files *multi;
999   struct multi_files *multi_type = NULL;
1000   struct multi_files *multi_name = NULL;
1001   multi = malloc(sizeof(struct multi_files));
1002   if (multi) {
1003     memset(multi, 0, sizeof(struct multi_files));
1004     multi->form.option = CURLFORM_FILE;
1005     multi->form.value = file_name;
1006   }
1007   else
1008     return NULL;
1009
1010   if (!*multi_start)
1011     *multi_start = multi;
1012
1013   if (type_name) {
1014     multi_type = malloc(sizeof(struct multi_files));
1015     if (multi_type) {
1016       memset(multi_type, 0, sizeof(struct multi_files));
1017       multi_type->form.option = CURLFORM_CONTENTTYPE;
1018       multi_type->form.value = type_name;
1019       multi->next = multi_type;
1020
1021       multi = multi_type;
1022     }
1023     else {
1024       free (multi);
1025       return NULL;
1026     }
1027   }
1028   if (show_filename) {
1029     multi_name = malloc(sizeof(struct multi_files));
1030     if (multi_name) {
1031       memset(multi_name, 0, sizeof(struct multi_files));
1032       multi_name->form.option = CURLFORM_FILENAME;
1033       multi_name->form.value = show_filename;
1034       multi->next = multi_name;
1035
1036       multi = multi_name;
1037     }
1038     else {
1039       free (multi);
1040       return NULL;
1041     }
1042   }
1043
1044   if (*multi_current)
1045     (*multi_current)->next = multi;
1046
1047   *multi_current = multi;
1048
1049   return *multi_current;
1050 }
1051
1052 /* Free the items of the list.
1053  */
1054 static void FreeMultiInfo (struct multi_files *multi_start)
1055 {
1056   struct multi_files *multi;
1057   while (multi_start) {
1058     multi = multi_start;
1059     multi_start = multi_start->next;
1060     free (multi);
1061   }
1062 }
1063
1064 /* Print list of OpenSSL engines supported.
1065  */
1066 static void list_engines (const struct curl_slist *engines)
1067 {
1068   puts ("Build-time engines:");
1069   if (!engines) {
1070     puts ("  <none>");
1071     return;
1072   }
1073   for ( ; engines; engines = engines->next)
1074     printf ("  %s\n", engines->data);
1075 }
1076
1077 /***************************************************************************
1078  *
1079  * formparse()
1080  *
1081  * Reads a 'name=value' parameter and builds the appropriate linked list.
1082  *
1083  * Specify files to upload with 'name=@filename'. Supports specified
1084  * given Content-Type of the files. Such as ';type=<content-type>'.
1085  *
1086  * If literal_value is set, any initial '@' or '<' in the value string
1087  * loses its special meaning, as does any embedded ';type='.
1088  *
1089  * You may specify more than one file for a single name (field). Specify
1090  * multiple files by writing it like:
1091  *
1092  * 'name=@filename,filename2,filename3'
1093  *
1094  * If you want content-types specified for each too, write them like:
1095  *
1096  * 'name=@filename;type=image/gif,filename2,filename3'
1097  *
1098  * If you want custom headers added for a single part, write them in a separate
1099  * file and do like this:
1100  *
1101  * 'name=foo;headers=@headerfile' or why not
1102  * 'name=@filemame;headers=@headerfile'
1103  *
1104  * To upload a file, but to fake the file name that will be included in the
1105  * formpost, do like this:
1106  *
1107  * 'name=@filename;filename=/dev/null'
1108  *
1109  * This function uses curl_formadd to fulfill it's job. Is heavily based on
1110  * the old curl_formparse code.
1111  *
1112  ***************************************************************************/
1113
1114 #define FORM_FILE_SEPARATOR ','
1115 #define FORM_TYPE_SEPARATOR ';'
1116
1117 static int formparse(struct Configurable *config,
1118                      const char *input,
1119                      struct curl_httppost **httppost,
1120                      struct curl_httppost **last_post,
1121                      bool literal_value)
1122 {
1123   /* nextarg MUST be a string in the format 'name=contents' and we'll
1124      build a linked list with the info */
1125   char name[256];
1126   char *contents;
1127   char major[128];
1128   char minor[128];
1129   char *contp;
1130   const char *type = NULL;
1131   char *sep;
1132   char *sep2;
1133
1134   if((1 == sscanf(input, "%255[^=]=", name)) &&
1135      (contp = strchr(input, '='))) {
1136     /* the input was using the correct format */
1137
1138     /* Allocate the contents */
1139     contents = strdup(contp+1);
1140     if(!contents) {
1141       fprintf(config->errors, "out of memory\n");
1142       return 1;
1143     }
1144     contp = contents;
1145
1146     if('@' == contp[0] && !literal_value) {
1147       struct multi_files *multi_start = NULL, *multi_current = NULL;
1148       /* we use the @-letter to indicate file name(s) */
1149       contp++;
1150
1151       multi_start = multi_current=NULL;
1152
1153       do {
1154         /* since this was a file, it may have a content-type specifier
1155            at the end too, or a filename. Or both. */
1156         char *ptr;
1157         char *filename=NULL;
1158
1159         sep=strchr(contp, FORM_TYPE_SEPARATOR);
1160         sep2=strchr(contp, FORM_FILE_SEPARATOR);
1161
1162         /* pick the closest */
1163         if(sep2 && (sep2 < sep)) {
1164           sep = sep2;
1165
1166           /* no type was specified! */
1167         }
1168
1169         type = NULL;
1170
1171         if(sep) {
1172
1173           /* if we got here on a comma, don't do much */
1174           if(FORM_FILE_SEPARATOR == *sep)
1175             ptr = NULL;
1176           else
1177             ptr = sep+1;
1178
1179           *sep=0; /* terminate file name at separator */
1180
1181           while(ptr && (FORM_FILE_SEPARATOR!= *ptr)) {
1182
1183             /* pass all white spaces */
1184             while(ISSPACE(*ptr))
1185               ptr++;
1186
1187             if(curlx_strnequal("type=", ptr, 5)) {
1188               /* set type pointer */
1189               type = &ptr[5];
1190
1191               /* verify that this is a fine type specifier */
1192               if(2 != sscanf(type, "%127[^/]/%127[^;,\n]",
1193                              major, minor)) {
1194                 warnf(config, "Illegally formatted content-type field!\n");
1195                 free(contents);
1196                 FreeMultiInfo (multi_start);
1197                 return 2; /* illegal content-type syntax! */
1198               }
1199               /* now point beyond the content-type specifier */
1200               sep = (char *)type + strlen(major)+strlen(minor)+1;
1201
1202               if(*sep) {
1203                 *sep=0; /* zero terminate type string */
1204
1205                 ptr=sep+1;
1206               }
1207               else
1208                 ptr = NULL; /* end */
1209             }
1210             else if(curlx_strnequal("filename=", ptr, 9)) {
1211               filename = &ptr[9];
1212               ptr=strchr(filename, FORM_TYPE_SEPARATOR);
1213               if(!ptr) {
1214                 ptr=strchr(filename, FORM_FILE_SEPARATOR);
1215               }
1216               if(ptr) {
1217                 *ptr=0; /* zero terminate */
1218                 ptr++;
1219               }
1220             }
1221             else
1222               /* confusion, bail out of loop */
1223               break;
1224           }
1225           /* find the following comma */
1226           if(ptr)
1227             sep=strchr(ptr, FORM_FILE_SEPARATOR);
1228           else
1229             sep=NULL;
1230         }
1231         else {
1232           sep=strchr(contp, FORM_FILE_SEPARATOR);
1233         }
1234         if(sep) {
1235           /* the next file name starts here */
1236           *sep =0;
1237           sep++;
1238         }
1239         /* if type == NULL curl_formadd takes care of the problem */
1240
1241         if (!AddMultiFiles (contp, type, filename, &multi_start,
1242                             &multi_current)) {
1243           warnf(config, "Error building form post!\n");
1244           free(contents);
1245           FreeMultiInfo (multi_start);
1246           return 3;
1247         }
1248         contp = sep; /* move the contents pointer to after the separator */
1249
1250       } while(sep && *sep); /* loop if there's another file name */
1251
1252       /* now we add the multiple files section */
1253       if (multi_start) {
1254         struct curl_forms *forms = NULL;
1255         struct multi_files *ptr = multi_start;
1256         unsigned int i, count = 0;
1257         while (ptr) {
1258           ptr = ptr->next;
1259           ++count;
1260         }
1261         forms = malloc((count+1)*sizeof(struct curl_forms));
1262         if (!forms)
1263         {
1264           fprintf(config->errors, "Error building form post!\n");
1265           free(contents);
1266           FreeMultiInfo (multi_start);
1267           return 4;
1268         }
1269         for (i = 0, ptr = multi_start; i < count; ++i, ptr = ptr->next)
1270         {
1271           forms[i].option = ptr->form.option;
1272           forms[i].value = ptr->form.value;
1273         }
1274         forms[count].option = CURLFORM_END;
1275         FreeMultiInfo (multi_start);
1276         if (curl_formadd(httppost, last_post,
1277                          CURLFORM_COPYNAME, name,
1278                          CURLFORM_ARRAY, forms, CURLFORM_END) != 0) {
1279           warnf(config, "curl_formadd failed!\n");
1280           free(forms);
1281           free(contents);
1282           return 5;
1283         }
1284         free(forms);
1285       }
1286     }
1287     else {
1288       struct curl_forms info[4];
1289       int i = 0;
1290       char *ct = literal_value? NULL: strstr(contp, ";type=");
1291
1292       info[i].option = CURLFORM_COPYNAME;
1293       info[i].value = name;
1294       i++;
1295
1296       if(ct) {
1297         info[i].option = CURLFORM_CONTENTTYPE;
1298         info[i].value = &ct[6];
1299         i++;
1300         ct[0]=0; /* zero terminate here */
1301       }
1302
1303       if( contp[0]=='<' && !literal_value) {
1304         info[i].option = CURLFORM_FILECONTENT;
1305         info[i].value = contp+1;
1306         i++;
1307         info[i].option = CURLFORM_END;
1308
1309         if (curl_formadd(httppost, last_post,
1310                          CURLFORM_ARRAY, info, CURLFORM_END ) != 0) {
1311           warnf(config, "curl_formadd failed, possibly the file %s is bad!\n",
1312                 contp+1);
1313           free(contents);
1314           return 6;
1315         }
1316       }
1317       else {
1318 #ifdef CURL_DOES_CONVERSIONS
1319         convert_to_network(contp, strlen(contp));
1320 #endif
1321         info[i].option = CURLFORM_COPYCONTENTS;
1322         info[i].value = contp;
1323         i++;
1324         info[i].option = CURLFORM_END;
1325         if (curl_formadd(httppost, last_post,
1326                          CURLFORM_ARRAY, info, CURLFORM_END) != 0) {
1327           warnf(config, "curl_formadd failed!\n");
1328           free(contents);
1329           return 7;
1330         }
1331       }
1332     }
1333
1334   }
1335   else {
1336     warnf(config, "Illegally formatted input field!\n");
1337     return 1;
1338   }
1339   free(contents);
1340   return 0;
1341 }
1342
1343
1344 typedef enum {
1345   PARAM_OK,
1346   PARAM_OPTION_AMBIGUOUS,
1347   PARAM_OPTION_UNKNOWN,
1348   PARAM_REQUIRES_PARAMETER,
1349   PARAM_BAD_USE,
1350   PARAM_HELP_REQUESTED,
1351   PARAM_GOT_EXTRA_PARAMETER,
1352   PARAM_BAD_NUMERIC,
1353   PARAM_LIBCURL_DOESNT_SUPPORT,
1354   PARAM_NO_MEM,
1355   PARAM_LAST
1356 } ParameterError;
1357
1358 static const char *param2text(int res)
1359 {
1360   ParameterError error = (ParameterError)res;
1361   switch(error) {
1362   case PARAM_GOT_EXTRA_PARAMETER:
1363     return "had unsupported trailing garbage";
1364   case PARAM_OPTION_UNKNOWN:
1365     return "is unknown";
1366   case PARAM_OPTION_AMBIGUOUS:
1367     return "is ambiguous";
1368   case PARAM_REQUIRES_PARAMETER:
1369     return "requires parameter";
1370   case PARAM_BAD_USE:
1371     return "is badly used here";
1372   case PARAM_BAD_NUMERIC:
1373     return "expected a proper numerical parameter";
1374   case PARAM_LIBCURL_DOESNT_SUPPORT:
1375     return "the installed libcurl version doesn't support this";
1376   case PARAM_NO_MEM:
1377     return "out of memory";
1378   default:
1379     return "unknown error";
1380   }
1381 }
1382
1383 static ParameterError file2string(char **bufp, FILE *file)
1384 {
1385   char buffer[256];
1386   char *ptr;
1387   char *string = NULL;
1388   size_t stringlen = 0;
1389   size_t buflen;
1390
1391   if(file) {
1392     while(fgets(buffer, sizeof(buffer), file)) {
1393       if((ptr = strchr(buffer, '\r')) != NULL)
1394         *ptr = '\0';
1395       if((ptr = strchr(buffer, '\n')) != NULL)
1396         *ptr = '\0';
1397       buflen = strlen(buffer);
1398       if((ptr = realloc(string, stringlen+buflen+1)) == NULL) {
1399         if(string)
1400           free(string);
1401         return PARAM_NO_MEM;
1402       }
1403       string = ptr;
1404       strcpy(string+stringlen, buffer);
1405       stringlen += buflen;
1406     }
1407   }
1408   *bufp = string;
1409   return PARAM_OK;
1410 }
1411
1412 static ParameterError file2memory(char **bufp, size_t *size, FILE *file)
1413 {
1414   char *newbuf;
1415   char *buffer = NULL;
1416   size_t alloc = 512;
1417   size_t nused = 0;
1418   size_t nread;
1419
1420   if(file) {
1421     do {
1422       if(!buffer || (alloc == nused)) {
1423         /* size_t overflow detection for huge files */
1424         if(alloc+1 > ((size_t)-1)/2) {
1425           if(buffer)
1426             free(buffer);
1427           return PARAM_NO_MEM;
1428         }
1429         alloc *= 2;
1430         /* allocate an extra char, reserved space, for null termination */
1431         if((newbuf = realloc(buffer, alloc+1)) == NULL) {
1432           if(buffer)
1433             free(buffer);
1434           return PARAM_NO_MEM;
1435         }
1436         buffer = newbuf;
1437       }
1438       nread = fread(buffer+nused, 1, alloc-nused, file);
1439       nused += nread;
1440     } while(nread);
1441     /* null terminate the buffer in case it's used as a string later */
1442     buffer[nused] = '\0';
1443     /* free trailing slack space, if possible */
1444     if(alloc != nused) {
1445       if((newbuf = realloc(buffer, nused+1)) != NULL)
1446         buffer = newbuf;
1447     }
1448     /* discard buffer if nothing was read */
1449     if(!nused) {
1450       free(buffer);
1451       buffer = NULL; /* no string */
1452     }
1453   }
1454   *size = nused;
1455   *bufp = buffer;
1456   return PARAM_OK;
1457 }
1458
1459 static void cleanarg(char *str)
1460 {
1461 #ifdef HAVE_WRITABLE_ARGV
1462   /* now that GetStr has copied the contents of nextarg, wipe the next
1463    * argument out so that the username:password isn't displayed in the
1464    * system process list */
1465   if (str) {
1466     size_t len = strlen(str);
1467     memset(str, ' ', len);
1468   }
1469 #else
1470   (void)str;
1471 #endif
1472 }
1473
1474 /*
1475  * Parse the string and write the integer in the given address. Return
1476  * non-zero on failure, zero on success.
1477  *
1478  * The string must start with a digit to be valid.
1479  *
1480  * Since this function gets called with the 'nextarg' pointer from within the
1481  * getparameter a lot, we must check it for NULL before accessing the str
1482  * data.
1483  */
1484
1485 static int str2num(long *val, const char *str)
1486 {
1487   int retcode = 0;
1488   if(str && ISDIGIT(*str))
1489     *val = atoi(str);
1490   else
1491     retcode = 1; /* badness */
1492   return retcode;
1493 }
1494
1495 /**
1496  * Parses the given string looking for an offset (which may be
1497  * a larger-than-integer value).
1498  *
1499  * @param val  the offset to populate
1500  * @param str  the buffer containing the offset
1501  * @return zero if successful, non-zero if failure.
1502  */
1503 static int str2offset(curl_off_t *val, const char *str)
1504 {
1505 #if (CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
1506   *val = curlx_strtoofft(str, NULL, 0);
1507   if((*val == CURL_LLONG_MAX || *val == CURL_LLONG_MIN) && (ERRNO == ERANGE))
1508     return 1;
1509 #else
1510   *val = strtol(str, NULL, 0);
1511   if ((*val == LONG_MIN || *val == LONG_MAX) && ERRNO == ERANGE)
1512     return 1;
1513 #endif
1514   return 0;
1515 }
1516
1517 static void checkpasswd(const char *kind, /* for what purpose */
1518                         char **userpwd) /* pointer to allocated string */
1519 {
1520   char *ptr;
1521   if(!*userpwd)
1522     return;
1523
1524   ptr = strchr(*userpwd, ':');
1525   if(!ptr) {
1526     /* no password present, prompt for one */
1527     char passwd[256]="";
1528     char prompt[256];
1529     size_t passwdlen;
1530     size_t userlen = strlen(*userpwd);
1531     char *passptr;
1532
1533     /* build a nice-looking prompt */
1534     curlx_msnprintf(prompt, sizeof(prompt),
1535                    "Enter %s password for user '%s':",
1536                    kind, *userpwd);
1537
1538     /* get password */
1539     getpass_r(prompt, passwd, sizeof(passwd));
1540     passwdlen = strlen(passwd);
1541
1542     /* extend the allocated memory area to fit the password too */
1543     passptr = realloc(*userpwd,
1544                       passwdlen + 1 + /* an extra for the colon */
1545                       userlen + 1);   /* an extra for the zero */
1546
1547     if(passptr) {
1548       /* append the password separated with a colon */
1549       passptr[userlen]=':';
1550       memcpy(&passptr[userlen+1], passwd, passwdlen+1);
1551       *userpwd = passptr;
1552     }
1553   }
1554 }
1555
1556 static ParameterError add2list(struct curl_slist **list,
1557                                const char *ptr)
1558 {
1559   struct curl_slist *newlist = curl_slist_append(*list, ptr);
1560   if(newlist)
1561     *list = newlist;
1562   else
1563     return PARAM_NO_MEM;
1564
1565   return PARAM_OK;
1566 }
1567
1568 static int ftpfilemethod(struct Configurable *config, const char *str)
1569 {
1570   if(curlx_raw_equal("singlecwd", str))
1571     return CURLFTPMETHOD_SINGLECWD;
1572   if(curlx_raw_equal("nocwd", str))
1573     return CURLFTPMETHOD_NOCWD;
1574   if(curlx_raw_equal("multicwd", str))
1575     return CURLFTPMETHOD_MULTICWD;
1576   warnf(config, "unrecognized ftp file method '%s', using default\n", str);
1577   return CURLFTPMETHOD_MULTICWD;
1578 }
1579
1580 static int ftpcccmethod(struct Configurable *config, const char *str)
1581 {
1582   if(curlx_raw_equal("passive", str))
1583     return CURLFTPSSL_CCC_PASSIVE;
1584   if(curlx_raw_equal("active", str))
1585     return CURLFTPSSL_CCC_ACTIVE;
1586   warnf(config, "unrecognized ftp CCC method '%s', using default\n", str);
1587   return CURLFTPSSL_CCC_PASSIVE;
1588 }
1589
1590
1591 static int sockoptcallback(void *clientp, curl_socket_t curlfd,
1592                            curlsocktype purpose)
1593 {
1594   struct Configurable *config = (struct Configurable *)clientp;
1595   int onoff = 1; /* this callback is only used if we ask for keepalives on the
1596                     connection */
1597 #if defined(TCP_KEEPIDLE) || defined(TCP_KEEPINTVL)
1598   int keepidle = (int)config->alivetime;
1599 #endif
1600
1601   switch (purpose) {
1602   case CURLSOCKTYPE_IPCXN:
1603     if(setsockopt(curlfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&onoff,
1604                   sizeof(onoff)) < 0) {
1605       /* don't abort operation, just issue a warning */
1606       SET_SOCKERRNO(0);
1607       warnf(clientp, "Could not set SO_KEEPALIVE!\n");
1608       return 0;
1609     }
1610     else {
1611       if (config->alivetime) {
1612 #ifdef TCP_KEEPIDLE
1613         if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&keepidle,
1614                       sizeof(keepidle)) < 0) {
1615           /* don't abort operation, just issue a warning */
1616           SET_SOCKERRNO(0);
1617           warnf(clientp, "Could not set TCP_KEEPIDLE!\n");
1618           return 0;
1619         }
1620 #endif
1621 #ifdef TCP_KEEPINTVL
1622         if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&keepidle,
1623                       sizeof(keepidle)) < 0) {
1624           /* don't abort operation, just issue a warning */
1625           SET_SOCKERRNO(0);
1626           warnf(clientp, "Could not set TCP_KEEPINTVL!\n");
1627           return 0;
1628         }
1629 #endif
1630       }
1631     }
1632     break;
1633   default:
1634     break;
1635   }
1636
1637   return 0;
1638 }
1639
1640
1641 static ParameterError getparameter(char *flag, /* f or -long-flag */
1642                                    char *nextarg, /* NULL if unset */
1643                                    bool *usedarg, /* set to TRUE if the arg
1644                                                      has been used */
1645                                    struct Configurable *config)
1646 {
1647   char letter;
1648   char subletter=0; /* subletters can only occur on long options */
1649   int rc; /* generic return code variable */
1650   const char *parse=NULL;
1651   unsigned int j;
1652   time_t now;
1653   int hit=-1;
1654   bool longopt=FALSE;
1655   bool singleopt=FALSE; /* when true means '-o foo' used '-ofoo' */
1656   ParameterError err;
1657   bool toggle=TRUE; /* how to switch boolean options, on or off. Controlled
1658                        by using --OPTION or --no-OPTION */
1659
1660   /* single-letter,
1661      long-name,
1662      boolean whether it takes an additional argument
1663      */
1664   static const struct LongShort aliases[]= {
1665     /* all these ones, starting with "*" or "$" as a short-option have *no*
1666        short option to mention. */
1667     {"*", "url",         TRUE},
1668     {"*a", "random-file", TRUE},
1669     {"*b", "egd-file",   TRUE},
1670     {"*c", "connect-timeout", TRUE},
1671     {"*d", "ciphers",    TRUE},
1672     {"*e", "disable-epsv", FALSE},
1673     {"*E", "epsv", FALSE}, /* made like this to make --no-epsv and --epsv to
1674                               work although --disable-epsv is the documented
1675                               option */
1676 #ifdef USE_ENVIRONMENT
1677     {"*f", "environment", FALSE},
1678 #endif
1679     {"*g", "trace",      TRUE},
1680     {"*h", "trace-ascii", TRUE},
1681     {"*i", "limit-rate", TRUE},
1682     {"*j", "compressed",  FALSE}, /* might take an arg someday */
1683     {"*k", "digest",     FALSE},
1684     {"*l", "negotiate",  FALSE},
1685     {"*m", "ntlm",       FALSE},
1686     {"*n", "basic",      FALSE},
1687     {"*o", "anyauth",    FALSE},
1688 #ifdef USE_WATT32
1689     {"*p", "wdebug",     FALSE},
1690 #endif
1691     {"*q", "ftp-create-dirs", FALSE},
1692     {"*r", "create-dirs", FALSE},
1693     {"*s", "max-redirs",   TRUE},
1694     {"*t", "proxy-ntlm",   FALSE},
1695     {"*u", "crlf",        FALSE},
1696     {"*v", "stderr",      TRUE},
1697     {"*w", "interface",   TRUE},
1698     {"*x", "krb" ,        TRUE},
1699     {"*x", "krb4" ,       TRUE}, /* this is the previous name */
1700     {"*y", "max-filesize", TRUE},
1701     {"*z", "disable-eprt", FALSE},
1702     {"*Z", "eprt", FALSE}, /* made like this to make --no-eprt and --eprt to
1703                               work although --disable-eprt is the documented
1704                               option */
1705     {"$a", "ftp-ssl",    FALSE}, /* deprecated name since 7.20.0 */
1706     {"$a", "ssl",        FALSE}, /* new option name in 7.20.0, previously this
1707                                     was ftp-ssl */
1708     {"$b", "ftp-pasv",   FALSE},
1709     {"$c", "socks5",   TRUE},
1710     {"$c", "socks",      TRUE}, /* this is how the option once was documented
1711                                    but we prefer the --socks5 version for
1712                                    explicit version */
1713     {"$d", "tcp-nodelay",FALSE},
1714     {"$e", "proxy-digest", FALSE},
1715     {"$f", "proxy-basic", FALSE},
1716     {"$g", "retry",      TRUE},
1717     {"$h", "retry-delay", TRUE},
1718     {"$i", "retry-max-time", TRUE},
1719     {"$k", "proxy-negotiate",   FALSE},
1720     {"$m", "ftp-account", TRUE},
1721     {"$n", "proxy-anyauth", FALSE},
1722     {"$o", "trace-time", FALSE},
1723     {"$p", "ignore-content-length", FALSE},
1724     {"$q", "ftp-skip-pasv-ip", FALSE},
1725     {"$r", "ftp-method", TRUE},
1726     {"$s", "local-port", TRUE},
1727     {"$t", "socks4",     TRUE},
1728     {"$T", "socks4a",    TRUE},
1729     {"$u", "ftp-alternative-to-user", TRUE},
1730     {"$v", "ftp-ssl-reqd", FALSE}, /* deprecated name since 7.20.0 */
1731     {"$v", "ssl-reqd", FALSE},  /* new option name in 7.20.0, previously this
1732                                    was ftp-ssl-reqd */
1733     {"$w", "sessionid", FALSE}, /* listed as --no-sessionid in the help */
1734     {"$x", "ftp-ssl-control", FALSE},
1735     {"$y", "ftp-ssl-ccc", FALSE},
1736     {"$j", "ftp-ssl-ccc-mode", TRUE},
1737     {"$z", "libcurl",    TRUE},
1738     {"$#", "raw",        FALSE},
1739     {"$0", "post301",    FALSE},
1740     {"$1", "keepalive",   FALSE}, /* listed as --no-keepalive in the help */
1741     {"$2", "socks5-hostname", TRUE},
1742     {"$3", "keepalive-time",  TRUE},
1743     {"$4", "post302",    FALSE},
1744     {"$5", "noproxy",    TRUE},
1745
1746 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1747     {"$6", "socks5-gssapi-service",  TRUE},
1748     {"$7", "socks5-gssapi-nec",  FALSE},
1749 #endif
1750     {"$8", "proxy1.0",   TRUE},
1751     {"$9", "tftp-blksize", TRUE},
1752     {"$A", "mail-from", TRUE},
1753     {"$B", "mail-rcpt", TRUE},
1754     {"$C", "ftp-pret",   FALSE},
1755     {"0", "http1.0",     FALSE},
1756     {"1", "tlsv1",       FALSE},
1757     {"2", "sslv2",       FALSE},
1758     {"3", "sslv3",       FALSE},
1759     {"4", "ipv4",       FALSE},
1760     {"6", "ipv6",       FALSE},
1761     {"a", "append",      FALSE},
1762     {"A", "user-agent",  TRUE},
1763     {"b", "cookie",      TRUE},
1764     {"B", "use-ascii",   FALSE},
1765     {"c", "cookie-jar",  TRUE},
1766     {"C", "continue-at", TRUE},
1767     {"d", "data",        TRUE},
1768     {"da", "data-ascii", TRUE},
1769     {"db", "data-binary", TRUE},
1770     {"de", "data-urlencode", TRUE},
1771     {"D", "dump-header", TRUE},
1772     {"e", "referer",     TRUE},
1773     {"E", "cert",        TRUE},
1774     {"Ea", "cacert",     TRUE},
1775     {"Eb","cert-type",   TRUE},
1776     {"Ec","key",         TRUE},
1777     {"Ed","key-type",    TRUE},
1778     {"Ee","pass",        TRUE},
1779     {"Ef","engine",      TRUE},
1780     {"Eg","capath ",     TRUE},
1781     {"Eh","pubkey",      TRUE},
1782     {"Ei", "hostpubmd5", TRUE},
1783     {"Ej","crlfile",     TRUE},
1784     {"f", "fail",        FALSE},
1785     {"F", "form",        TRUE},
1786     {"Fs","form-string", TRUE},
1787     {"g", "globoff",     FALSE},
1788     {"G", "get",         FALSE},
1789     {"h", "help",        FALSE},
1790     {"H", "header",      TRUE},
1791     {"i", "include",     FALSE},
1792     {"I", "head",        FALSE},
1793     {"j", "junk-session-cookies", FALSE},
1794     {"J", "remote-header-name", FALSE},
1795     {"k", "insecure",    FALSE},
1796     {"K", "config",      TRUE},
1797     {"l", "list-only",   FALSE},
1798     {"L", "location",    FALSE},
1799     {"Lt", "location-trusted", FALSE},
1800     {"m", "max-time",    TRUE},
1801     {"M", "manual",      FALSE},
1802     {"n", "netrc",       FALSE},
1803     {"no", "netrc-optional", FALSE},
1804     {"N", "buffer",   FALSE}, /* listed as --no-buffer in the help */
1805     {"o", "output",      TRUE},
1806     {"O",  "remote-name", FALSE},
1807     {"Oa", "remote-name-all", FALSE},
1808     {"p", "proxytunnel", FALSE},
1809     {"P", "ftpport",     TRUE}, /* older version */
1810     {"P", "ftp-port",    TRUE},
1811     {"q", "disable",     FALSE},
1812     {"Q", "quote",       TRUE},
1813     {"r", "range",       TRUE},
1814     {"R", "remote-time", FALSE},
1815     {"s", "silent",      FALSE},
1816     {"S", "show-error",  FALSE},
1817     {"t", "telnet-options", TRUE}, /* this is documented as telnet-option */
1818     {"T", "upload-file", TRUE},
1819     {"u", "user",        TRUE},
1820     {"U", "proxy-user",  TRUE},
1821     {"v", "verbose",     FALSE},
1822     {"V", "version",     FALSE},
1823     {"w", "write-out",   TRUE},
1824     {"x", "proxy",       TRUE},
1825     {"X", "request",     TRUE},
1826     {"X", "http-request", TRUE}, /* OBSOLETE VERSION */
1827     {"Y", "speed-limit",  TRUE},
1828     {"y", "speed-time", TRUE},
1829     {"z", "time-cond",   TRUE},
1830     {"#", "progress-bar",FALSE},
1831   };
1832
1833   if(('-' != flag[0]) ||
1834      (('-' == flag[0]) && ('-' == flag[1]))) {
1835     /* this should be a long name */
1836     char *word=('-' == flag[0])?flag+2:flag;
1837     size_t fnam=strlen(word);
1838     int numhits=0;
1839
1840     if(!strncmp(word, "no-", 3)) {
1841       /* disable this option but ignore the "no-" part when looking for it */
1842       word += 3;
1843       toggle = FALSE;
1844     }
1845
1846     for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
1847       if(curlx_strnequal(aliases[j].lname, word, fnam)) {
1848         longopt = TRUE;
1849         numhits++;
1850         if(curlx_raw_equal(aliases[j].lname, word)) {
1851           parse = aliases[j].letter;
1852           hit = j;
1853           numhits = 1; /* a single unique hit */
1854           break;
1855         }
1856         parse = aliases[j].letter;
1857         hit = j;
1858       }
1859     }
1860     if(numhits>1) {
1861       /* this is at least the second match! */
1862       return PARAM_OPTION_AMBIGUOUS;
1863     }
1864     if(hit < 0) {
1865       return PARAM_OPTION_UNKNOWN;
1866     }
1867   }
1868   else {
1869     flag++; /* prefixed with one dash, pass it */
1870     hit=-1;
1871     parse = flag;
1872   }
1873
1874   do {
1875     /* we can loop here if we have multiple single-letters */
1876
1877     if(!longopt) {
1878       if(NULL != parse) {
1879         letter = (char)*parse;
1880       }
1881       else {
1882         letter = '\0';
1883       }
1884       subletter='\0';
1885     }
1886     else {
1887       letter = parse[0];
1888       subletter = parse[1];
1889     }
1890     *usedarg = FALSE; /* default is that we don't use the arg */
1891
1892     if(hit < 0) {
1893       for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
1894         if(letter == aliases[j].letter[0]) {
1895           hit = j;
1896           break;
1897         }
1898       }
1899       if(hit < 0) {
1900         return PARAM_OPTION_UNKNOWN;
1901       }
1902     }
1903
1904     if(aliases[hit].extraparam) {
1905       /* this option requires an extra parameter */
1906       if(!longopt && parse[1]) {
1907         nextarg=(char *)&parse[1]; /* this is the actual extra parameter */
1908         singleopt=TRUE;   /* don't loop anymore after this */
1909       }
1910       else if(!nextarg)
1911         return PARAM_REQUIRES_PARAMETER;
1912       else
1913         *usedarg = TRUE; /* mark it as used */
1914     }
1915
1916     switch(letter) {
1917     case '*': /* options without a short option */
1918       switch(subletter) {
1919       case 'a': /* random-file */
1920         GetStr(&config->random_file, nextarg);
1921         break;
1922       case 'b': /* egd-file */
1923         GetStr(&config->egd_file, nextarg);
1924         break;
1925       case 'c': /* connect-timeout */
1926         if(str2num(&config->connecttimeout, nextarg))
1927           return PARAM_BAD_NUMERIC;
1928         break;
1929       case 'd': /* ciphers */
1930         GetStr(&config->cipher_list, nextarg);
1931         break;
1932       case 'e': /* --disable-epsv */
1933         config->disable_epsv = toggle;
1934         break;
1935       case 'E': /* --epsv */
1936         config->disable_epsv = (bool)(!toggle);
1937         break;
1938 #ifdef USE_ENVIRONMENT
1939       case 'f':
1940         config->writeenv = toggle;
1941         break;
1942 #endif
1943       case 'g': /* --trace */
1944         GetStr(&config->trace_dump, nextarg);
1945         if(config->tracetype && (config->tracetype != TRACE_BIN))
1946           warnf(config, "--trace overrides an earlier trace/verbose option\n");
1947         config->tracetype = TRACE_BIN;
1948         break;
1949       case 'h': /* --trace-ascii */
1950         GetStr(&config->trace_dump, nextarg);
1951         if(config->tracetype && (config->tracetype != TRACE_ASCII))
1952           warnf(config,
1953                 "--trace-ascii overrides an earlier trace/verbose option\n");
1954         config->tracetype = TRACE_ASCII;
1955         break;
1956       case 'i': /* --limit-rate */
1957         {
1958           /* We support G, M, K too */
1959           char *unit;
1960           curl_off_t value = curlx_strtoofft(nextarg, &unit, 0);
1961
1962           if(!*unit)
1963             unit=(char *)"b";
1964           else if(strlen(unit) > 1)
1965             unit=(char *)"w"; /* unsupported */
1966
1967           switch(*unit) {
1968           case 'G':
1969           case 'g':
1970             value *= 1024*1024*1024;
1971             break;
1972           case 'M':
1973           case 'm':
1974             value *= 1024*1024;
1975             break;
1976           case 'K':
1977           case 'k':
1978             value *= 1024;
1979             break;
1980           case 'b':
1981           case 'B':
1982             /* for plain bytes, leave as-is */
1983             break;
1984           default:
1985             warnf(config, "unsupported rate unit. Use G, M, K or B!\n");
1986             return PARAM_BAD_USE;
1987           }
1988           config->recvpersecond = value;
1989           config->sendpersecond = value;
1990         }
1991         break;
1992
1993       case 'j': /* --compressed */
1994         config->encoding = toggle;
1995         break;
1996
1997       case 'k': /* --digest */
1998         if(toggle)
1999           config->authtype |= CURLAUTH_DIGEST;
2000         else
2001           config->authtype &= ~CURLAUTH_DIGEST;
2002         break;
2003
2004       case 'l': /* --negotiate */
2005         if(toggle) {
2006           if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE)
2007             config->authtype |= CURLAUTH_GSSNEGOTIATE;
2008           else
2009             return PARAM_LIBCURL_DOESNT_SUPPORT;
2010         }
2011         else
2012           config->authtype &= ~CURLAUTH_GSSNEGOTIATE;
2013         break;
2014
2015       case 'm': /* --ntlm */
2016         if(toggle) {
2017           if(curlinfo->features & CURL_VERSION_NTLM)
2018             config->authtype |= CURLAUTH_NTLM;
2019           else
2020             return PARAM_LIBCURL_DOESNT_SUPPORT;
2021         }
2022         else
2023           config->authtype &= ~CURLAUTH_NTLM;
2024         break;
2025
2026       case 'n': /* --basic for completeness */
2027         if(toggle)
2028           config->authtype |= CURLAUTH_BASIC;
2029         else
2030           config->authtype &= ~CURLAUTH_BASIC;
2031         break;
2032
2033       case 'o': /* --anyauth, let libcurl pick it */
2034         if(toggle)
2035           config->authtype = CURLAUTH_ANY;
2036         /* --no-anyauth simply doesn't touch it */
2037         break;
2038
2039 #ifdef USE_WATT32
2040       case 'p': /* --wdebug */
2041         dbug_init();
2042         break;
2043 #endif
2044       case 'q': /* --ftp-create-dirs */
2045         config->ftp_create_dirs = toggle;
2046         break;
2047
2048       case 'r': /* --create-dirs */
2049         config->create_dirs = TRUE;
2050         break;
2051
2052       case 's': /* --max-redirs */
2053         /* specified max no of redirects (http(s)) */
2054         if(str2num(&config->maxredirs, nextarg))
2055           return PARAM_BAD_NUMERIC;
2056         break;
2057
2058       case 't': /* --proxy-ntlm */
2059         if(curlinfo->features & CURL_VERSION_NTLM)
2060           config->proxyntlm = toggle;
2061         else
2062           return PARAM_LIBCURL_DOESNT_SUPPORT;
2063         break;
2064
2065       case 'u': /* --crlf */
2066         /* LF -> CRLF conversion? */
2067         config->crlf = TRUE;
2068         break;
2069
2070       case 'v': /* --stderr */
2071         if(strcmp(nextarg, "-")) {
2072           FILE *newfile = fopen(nextarg, "wt");
2073           if(!newfile)
2074             warnf(config, "Failed to open %s!\n", nextarg);
2075           else {
2076             if(config->errors_fopened)
2077               fclose(config->errors);
2078             config->errors = newfile;
2079             config->errors_fopened = TRUE;
2080           }
2081         }
2082         else
2083           config->errors = stdout;
2084       break;
2085       case 'w': /* --interface */
2086         /* interface */
2087         GetStr(&config->iface, nextarg);
2088         break;
2089       case 'x': /* --krb */
2090         /* kerberos level string */
2091         if(curlinfo->features & (CURL_VERSION_KERBEROS4 |
2092                                  CURL_VERSION_GSSNEGOTIATE))
2093           GetStr(&config->krblevel, nextarg);
2094         else
2095           return PARAM_LIBCURL_DOESNT_SUPPORT;
2096         break;
2097       case 'y': /* --max-filesize */
2098         if(str2offset(&config->max_filesize, nextarg))
2099           return PARAM_BAD_NUMERIC;
2100         break;
2101       case 'z': /* --disable-eprt */
2102         config->disable_eprt = toggle;
2103         break;
2104       case 'Z': /* --eprt */
2105         config->disable_eprt = (bool)(!toggle);
2106         break;
2107
2108       default: /* the URL! */
2109         {
2110           struct getout *url;
2111           if(config->url_get || (config->url_get=config->url_list)) {
2112             /* there's a node here, if it already is filled-in continue to find
2113                an "empty" node */
2114             while(config->url_get && (config->url_get->flags&GETOUT_URL))
2115               config->url_get = config->url_get->next;
2116           }
2117
2118           /* now there might or might not be an available node to fill in! */
2119
2120           if(config->url_get)
2121             /* existing node */
2122             url = config->url_get;
2123           else
2124             /* there was no free node, create one! */
2125             url=new_getout(config);
2126
2127           if(url) {
2128             /* fill in the URL */
2129             GetStr(&url->url, nextarg);
2130             url->flags |= GETOUT_URL;
2131           }
2132         }
2133       }
2134       break;
2135     case '$': /* more options without a short option */
2136       switch(subletter) {
2137       case 'a': /* --ftp-ssl */
2138         config->ftp_ssl = toggle;
2139         break;
2140       case 'b': /* --ftp-pasv */
2141         if(config->ftpport)
2142           free(config->ftpport);
2143         config->ftpport = NULL;
2144         break;
2145       case 'c': /* --socks5 specifies a socks5 proxy to use, and resolves
2146                    the name locally and passes on the resolved address */
2147         GetStr(&config->socksproxy, nextarg);
2148         config->socksver = CURLPROXY_SOCKS5;
2149         break;
2150       case 't': /* --socks4 specifies a socks4 proxy to use */
2151         GetStr(&config->socksproxy, nextarg);
2152         config->socksver = CURLPROXY_SOCKS4;
2153         break;
2154       case 'T': /* --socks4a specifies a socks4a proxy to use */
2155         GetStr(&config->socksproxy, nextarg);
2156         config->socksver = CURLPROXY_SOCKS4A;
2157         break;
2158       case '2': /* --socks5-hostname specifies a socks5 proxy and enables name
2159                    resolving with the proxy */
2160         GetStr(&config->socksproxy, nextarg);
2161         config->socksver = CURLPROXY_SOCKS5_HOSTNAME;
2162         break;
2163       case 'd': /* --tcp-nodelay option */
2164         config->tcp_nodelay = toggle;
2165         break;
2166       case 'e': /* --proxy-digest */
2167         config->proxydigest = toggle;
2168         break;
2169       case 'f': /* --proxy-basic */
2170         config->proxybasic = toggle;
2171         break;
2172       case 'g': /* --retry */
2173         if(str2num(&config->req_retry, nextarg))
2174           return PARAM_BAD_NUMERIC;
2175         break;
2176       case 'h': /* --retry-delay */
2177         if(str2num(&config->retry_delay, nextarg))
2178           return PARAM_BAD_NUMERIC;
2179         break;
2180       case 'i': /* --retry-max-time */
2181         if(str2num(&config->retry_maxtime, nextarg))
2182           return PARAM_BAD_NUMERIC;
2183         break;
2184
2185       case 'k': /* --proxy-negotiate */
2186         if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE)
2187           config->proxynegotiate = toggle;
2188         else
2189           return PARAM_LIBCURL_DOESNT_SUPPORT;
2190         break;
2191       case 'm': /* --ftp-account */
2192         GetStr(&config->ftp_account, nextarg);
2193         break;
2194       case 'n': /* --proxy-anyauth */
2195         config->proxyanyauth = toggle;
2196         break;
2197       case 'o': /* --trace-time */
2198         config->tracetime = toggle;
2199         break;
2200       case 'p': /* --ignore-content-length */
2201         config->ignorecl = toggle;
2202         break;
2203       case 'q': /* --ftp-skip-pasv-ip */
2204         config->ftp_skip_ip = toggle;
2205         break;
2206       case 'r': /* --ftp-method (undocumented at this point) */
2207         config->ftp_filemethod = ftpfilemethod(config, nextarg);
2208         break;
2209       case 's': /* --local-port */
2210         rc = sscanf(nextarg, "%d - %d",
2211                     &config->localport,
2212                     &config->localportrange);
2213         if(!rc)
2214           return PARAM_BAD_USE;
2215         else if(rc == 1)
2216           config->localportrange = 1; /* default number of ports to try */
2217         else {
2218           config->localportrange -= config->localport;
2219           if(config->localportrange < 1) {
2220             warnf(config, "bad range input\n");
2221             return PARAM_BAD_USE;
2222           }
2223         }
2224         break;
2225       case 'u': /* --ftp-alternative-to-user */
2226         GetStr(&config->ftp_alternative_to_user, nextarg);
2227         break;
2228       case 'v': /* --ftp-ssl-reqd */
2229         config->ftp_ssl_reqd = toggle;
2230         break;
2231       case 'w': /* --no-sessionid */
2232         config->disable_sessionid = (bool)(!toggle);
2233         break;
2234       case 'x': /* --ftp-ssl-control */
2235         config->ftp_ssl_control = toggle;
2236         break;
2237       case 'y': /* --ftp-ssl-ccc */
2238         config->ftp_ssl_ccc = toggle;
2239         if(!config->ftp_ssl_ccc_mode)
2240             config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE;
2241         break;
2242       case 'j': /* --ftp-ssl-ccc-mode */
2243         config->ftp_ssl_ccc = TRUE;
2244         config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg);
2245         break;
2246       case 'z': /* --libcurl */
2247         GetStr(&config->libcurl, nextarg);
2248         break;
2249       case '#': /* --raw */
2250         config->raw = toggle;
2251         break;
2252       case '0': /* --post301 */
2253         config->post301 = toggle;
2254         break;
2255       case '1': /* --no-keepalive */
2256         config->nokeepalive = (bool)(!toggle);
2257         break;
2258       case '3': /* --keepalive-time */
2259         if(str2num(&config->alivetime, nextarg))
2260           return PARAM_BAD_NUMERIC;
2261         break;
2262       case '4': /* --post302 */
2263         config->post302 = toggle;
2264         break;
2265       case '5': /* --noproxy */
2266         /* This specifies the noproxy list */
2267         GetStr(&config->noproxy, nextarg);
2268         break;
2269 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
2270       case '6': /* --socks5-gssapi-service */
2271         GetStr(&config->socks5_gssapi_service, nextarg);
2272         break;
2273       case '7': /* --socks5-gssapi-nec*/
2274         config->socks5_gssapi_nec = TRUE;
2275         break;
2276 #endif
2277       case '8': /* --proxy1.0 */
2278         /* http 1.0 proxy */
2279         GetStr(&config->proxy, nextarg);
2280         config->proxyver = CURLPROXY_HTTP_1_0;
2281         break;
2282       case '9': /* --tftp-blksize */
2283         str2num(&config->tftp_blksize, nextarg);
2284         break;
2285       case 'A': /* --mail-from */
2286         GetStr(&config->mail_from, nextarg);
2287         break;
2288       case 'B': /* --mail-rcpt */
2289         /* append receiver to a list */
2290         err = add2list(&config->mail_rcpt, nextarg);
2291         if(err)
2292           return err;
2293         break;
2294       case 'C': /* --ftp-pret */
2295         config->ftp_pret = toggle;
2296         break;
2297       }
2298       break;
2299     case '#': /* --progress-bar */
2300       if(toggle)
2301         config->progressmode = CURL_PROGRESS_BAR;
2302       else
2303         config->progressmode = CURL_PROGRESS_STATS;
2304       break;
2305     case '0':
2306       /* HTTP version 1.0 */
2307       config->httpversion = CURL_HTTP_VERSION_1_0;
2308       break;
2309     case '1':
2310       /* TLS version 1 */
2311       config->ssl_version = CURL_SSLVERSION_TLSv1;
2312       break;
2313     case '2':
2314       /* SSL version 2 */
2315       config->ssl_version = CURL_SSLVERSION_SSLv2;
2316       break;
2317     case '3':
2318       /* SSL version 3 */
2319       config->ssl_version = CURL_SSLVERSION_SSLv3;
2320       break;
2321     case '4':
2322       /* IPv4 */
2323       config->ip_version = 4;
2324       break;
2325     case '6':
2326       /* IPv6 */
2327       config->ip_version = 6;
2328       break;
2329     case 'a':
2330       /* This makes the FTP sessions use APPE instead of STOR */
2331       config->ftp_append = toggle;
2332       break;
2333     case 'A':
2334       /* This specifies the User-Agent name */
2335       GetStr(&config->useragent, nextarg);
2336       break;
2337     case 'b': /* cookie string coming up: */
2338       if(nextarg[0] == '@') {
2339         nextarg++;
2340       }
2341       else if(strchr(nextarg, '=')) {
2342         /* A cookie string must have a =-letter */
2343         GetStr(&config->cookie, nextarg);
2344         break;
2345       }
2346       /* We have a cookie file to read from! */
2347       GetStr(&config->cookiefile, nextarg);
2348       break;
2349     case 'B':
2350       /* use ASCII/text when transfering */
2351       config->use_ascii = toggle;
2352       break;
2353     case 'c':
2354       /* get the file name to dump all cookies in */
2355       GetStr(&config->cookiejar, nextarg);
2356       break;
2357     case 'C':
2358       /* This makes us continue an ftp transfer at given position */
2359       if(!curlx_strequal(nextarg, "-")) {
2360         if(str2offset(&config->resume_from, nextarg))
2361           return PARAM_BAD_NUMERIC;
2362         config->resume_from_current = FALSE;
2363       }
2364       else {
2365         config->resume_from_current = TRUE;
2366         config->resume_from = 0;
2367       }
2368       config->use_resume=TRUE;
2369       break;
2370     case 'd':
2371       /* postfield data */
2372       {
2373         char *postdata=NULL;
2374         FILE *file;
2375
2376         if(subletter == 'e') { /* --data-urlencode*/
2377           /* [name]=[content], we encode the content part only
2378            * [name]@[file name]
2379            *
2380            * Case 2: we first load the file using that name and then encode
2381            * the content.
2382            */
2383           const char *p = strchr(nextarg, '=');
2384           size_t size = 0;
2385           size_t nlen;
2386           char is_file;
2387           if(!p)
2388             /* there was no '=' letter, check for a '@' instead */
2389             p = strchr(nextarg, '@');
2390           if (p) {
2391             nlen = p - nextarg; /* length of the name part */
2392             is_file = *p++; /* pass the separator */
2393           }
2394           else {
2395             /* neither @ nor =, so no name and it isn't a file */
2396             nlen = is_file = 0;
2397             p = nextarg;
2398           }
2399           if('@' == is_file) {
2400             /* a '@' letter, it means that a file name or - (stdin) follows */
2401
2402             if(curlx_strequal("-", p)) {
2403               file = stdin;
2404               SET_BINMODE(stdin);
2405             }
2406             else {
2407               file = fopen(p, "rb");
2408               if(!file)
2409                 warnf(config,
2410                       "Couldn't read data from file \"%s\", this makes "
2411                       "an empty POST.\n", nextarg);
2412             }
2413
2414             err = file2memory(&postdata, &size, file);
2415
2416             if(file && (file != stdin))
2417               fclose(file);
2418             if(err)
2419               return err;
2420           }
2421           else {
2422             GetStr(&postdata, p);
2423             size = strlen(postdata);
2424           }
2425
2426           if(!postdata) {
2427             /* no data from the file, point to a zero byte string to make this
2428                get sent as a POST anyway */
2429             postdata=strdup("");
2430           }
2431           else {
2432             char *enc = curl_easy_escape(config->easy, postdata, (int)size);
2433             free(postdata); /* no matter if it worked or not */
2434             if(enc) {
2435               /* now make a string with the name from above and append the
2436                  encoded string */
2437               size_t outlen = nlen + strlen(enc) + 2;
2438               char *n = malloc(outlen);
2439               if(!n) {
2440                 curl_free(enc);
2441                 return PARAM_NO_MEM;
2442               }
2443               if (nlen > 0) /* only append '=' if we have a name */
2444                 snprintf(n, outlen, "%.*s=%s", nlen, nextarg, enc);
2445               else
2446                 strcpy(n, enc);
2447               curl_free(enc);
2448               postdata = n;
2449             }
2450             else
2451               return PARAM_NO_MEM;
2452           }
2453         }
2454         else if('@' == *nextarg) {
2455           size_t size = 0;
2456           /* the data begins with a '@' letter, it means that a file name
2457              or - (stdin) follows */
2458           nextarg++; /* pass the @ */
2459
2460           if(curlx_strequal("-", nextarg)) {
2461             file = stdin;
2462             if(subletter == 'b') /* forced data-binary */
2463               SET_BINMODE(stdin);
2464           }
2465           else {
2466             file = fopen(nextarg, "rb");
2467             if(!file)
2468               warnf(config, "Couldn't read data from file \"%s\", this makes "
2469                     "an empty POST.\n", nextarg);
2470           }
2471
2472           if(subletter == 'b') {
2473             /* forced binary */
2474             err = file2memory(&postdata, &size, file);
2475             config->postfieldsize = (curl_off_t)size;
2476           }
2477           else
2478             err = file2string(&postdata, file);
2479
2480           if(file && (file != stdin))
2481             fclose(file);
2482           if(err)
2483             return err;
2484
2485           if(!postdata) {
2486             /* no data from the file, point to a zero byte string to make this
2487                get sent as a POST anyway */
2488             postdata=strdup("");
2489           }
2490         }
2491         else {
2492           GetStr(&postdata, nextarg);
2493         }
2494
2495 #ifdef CURL_DOES_CONVERSIONS
2496         if(subletter != 'b') { /* NOT forced binary, convert to ASCII */
2497           convert_to_network(postdata, strlen(postdata));
2498         }
2499 #endif
2500
2501         if(config->postfields) {
2502           /* we already have a string, we append this one
2503              with a separating &-letter */
2504           char *oldpost=config->postfields;
2505           size_t newlen = strlen(oldpost) + strlen(postdata) + 2;
2506           config->postfields=malloc(newlen);
2507           if(!config->postfields) {
2508             free(postdata);
2509             return PARAM_NO_MEM;
2510           }
2511           /* use ASCII value 0x26 for '&' to accommodate non-ASCII platforms */
2512           snprintf(config->postfields, newlen, "%s\x26%s", oldpost, postdata);
2513           free(oldpost);
2514           free(postdata);
2515         }
2516         else
2517           config->postfields=postdata;
2518       }
2519       /*
2520         We can't set the request type here, as this data might be used in
2521         a simple GET if -G is used. Already or soon.
2522
2523         if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
2524           return PARAM_BAD_USE;
2525       */
2526       break;
2527     case 'D':
2528       /* dump-header to given file name */
2529       GetStr(&config->headerfile, nextarg);
2530       break;
2531     case 'e':
2532       {
2533         char *ptr = strstr(nextarg, ";auto");
2534         if(ptr) {
2535           /* Automatic referer requested, this may be combined with a
2536              set initial one */
2537           config->autoreferer = TRUE;
2538           *ptr = 0; /* zero terminate here */
2539         }
2540         else
2541           config->autoreferer = FALSE;
2542         GetStr(&config->referer, nextarg);
2543       }
2544       break;
2545     case 'E':
2546       switch(subletter) {
2547       case 'a': /* CA info PEM file */
2548         /* CA info PEM file */
2549         GetStr(&config->cacert, nextarg);
2550         break;
2551       case 'b': /* cert file type */
2552         GetStr(&config->cert_type, nextarg);
2553         break;
2554       case 'c': /* private key file */
2555         GetStr(&config->key, nextarg);
2556         break;
2557       case 'd': /* private key file type */
2558         GetStr(&config->key_type, nextarg);
2559         break;
2560       case 'e': /* private key passphrase */
2561         GetStr(&config->key_passwd, nextarg);
2562         cleanarg(nextarg);
2563         break;
2564       case 'f': /* crypto engine */
2565         GetStr(&config->engine, nextarg);
2566         if (config->engine && curlx_raw_equal(config->engine,"list"))
2567            config->list_engines = TRUE;
2568         break;
2569       case 'g': /* CA info PEM file */
2570         /* CA cert directory */
2571         GetStr(&config->capath, nextarg);
2572         break;
2573       case 'h': /* --pubkey public key file */
2574         GetStr(&config->pubkey, nextarg);
2575         break;
2576       case 'i': /* --hostpubmd5 md5 of the host public key */
2577         GetStr(&config->hostpubmd5, nextarg);
2578         if (!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
2579            return PARAM_BAD_USE;
2580         break;
2581       case 'j': /* CRL info PEM file */
2582         /* CRL file */
2583         GetStr(&config->crlfile, nextarg);
2584         break;
2585       default: /* certificate file */
2586         {
2587           char *ptr = strchr(nextarg, ':');
2588           /* Since we live in a world of weirdness and confusion, the win32
2589              dudes can use : when using drive letters and thus
2590              c:\file:password needs to work. In order not to break
2591              compatibility, we still use : as separator, but we try to detect
2592              when it is used for a file name! On windows. */
2593 #ifdef WIN32
2594           if(ptr &&
2595              (ptr == &nextarg[1]) &&
2596              (nextarg[2] == '\\' || nextarg[2] == '/') &&
2597              (ISALPHA(nextarg[0])) )
2598              /* colon in the second column, followed by a backslash, and the
2599                 first character is an alphabetic letter:
2600
2601                 this is a drive letter colon */
2602             ptr = strchr(&nextarg[3], ':'); /* find the next one instead */
2603 #endif
2604           if(ptr) {
2605             /* we have a password too */
2606             *ptr=0;
2607             ptr++;
2608             GetStr(&config->key_passwd, ptr);
2609           }
2610           GetStr(&config->cert, nextarg);
2611           cleanarg(nextarg);
2612         }
2613       }
2614       break;
2615     case 'f':
2616       /* fail hard on errors  */
2617       config->failonerror = toggle;
2618       break;
2619     case 'F':
2620       /* "form data" simulation, this is a little advanced so lets do our best
2621          to sort this out slowly and carefully */
2622       if(formparse(config,
2623                    nextarg,
2624                    &config->httppost,
2625                    &config->last_post,
2626                    (bool) (subletter=='s'))) /* 's' means literal string */
2627         return PARAM_BAD_USE;
2628       if(SetHTTPrequest(config, HTTPREQ_POST, &config->httpreq))
2629         return PARAM_BAD_USE;
2630       break;
2631
2632     case 'g': /* g disables URLglobbing */
2633       config->globoff = toggle;
2634       break;
2635
2636     case 'G': /* HTTP GET */
2637       config->use_httpget = TRUE;
2638       break;
2639
2640     case 'h': /* h for help */
2641       if(toggle) {
2642         help();
2643         return PARAM_HELP_REQUESTED;
2644       }
2645       /* we now actually support --no-help too! */
2646       break;
2647     case 'H':
2648       /* A custom header to append to a list */
2649       err = add2list(&config->headers, nextarg);
2650       if(err)
2651         return err;
2652       break;
2653     case 'i':
2654       config->include_headers = toggle; /* include the headers as well in the
2655                                            general output stream */
2656       break;
2657     case 'j':
2658       config->cookiesession = toggle;
2659       break;
2660     case 'I':
2661       /*
2662        * no_body will imply include_headers later on
2663        */
2664       config->no_body = toggle;
2665       if(SetHTTPrequest(config,
2666                         (config->no_body)?HTTPREQ_HEAD:HTTPREQ_GET,
2667                         &config->httpreq))
2668         return PARAM_BAD_USE;
2669       break;
2670     case 'J': /* --remote-header-name */
2671       if (config->include_headers) {
2672         warnf(config,
2673               "--include and --remote-header-name cannot be combined.\n");
2674         return PARAM_BAD_USE;
2675       }
2676       config->content_disposition = toggle;
2677       break;
2678     case 'k': /* allow insecure SSL connects */
2679       config->insecure_ok = toggle;
2680       break;
2681     case 'K': /* parse config file */
2682       if(parseconfig(nextarg, config))
2683         warnf(config, "error trying read config from the '%s' file\n",
2684               nextarg);
2685       break;
2686     case 'l':
2687       config->dirlistonly = toggle; /* only list the names of the FTP dir */
2688       break;
2689     case 'L':
2690       config->followlocation = toggle; /* Follow Location: HTTP headers */
2691       switch (subletter) {
2692       case 't':
2693         /* Continue to send authentication (user+password) when following
2694          * locations, even when hostname changed */
2695         config->unrestricted_auth = toggle;
2696         break;
2697       }
2698       break;
2699     case 'm':
2700       /* specified max time */
2701       if(str2num(&config->timeout, nextarg))
2702         return PARAM_BAD_NUMERIC;
2703       break;
2704     case 'M': /* M for manual, huge help */
2705       if(toggle) { /* --no-manual shows no manual... */
2706 #ifdef USE_MANUAL
2707         hugehelp();
2708         return PARAM_HELP_REQUESTED;
2709 #else
2710         warnf(config,
2711               "built-in manual was disabled at build-time!\n");
2712         return PARAM_OPTION_UNKNOWN;
2713 #endif
2714       }
2715       break;
2716     case 'n':
2717       switch(subletter) {
2718       case 'o': /* CA info PEM file */
2719         /* use .netrc or URL */
2720         config->netrc_opt = toggle;
2721         break;
2722       default:
2723         /* pick info from .netrc, if this is used for http, curl will
2724            automatically enfore user+password with the request */
2725         config->netrc = toggle;
2726         break;
2727       }
2728       break;
2729     case 'N':
2730       /* disable the output I/O buffering. note that the option is called
2731          --buffer but is mostly used in the negative form: --no-buffer */
2732       if(longopt)
2733         config->nobuffer = (bool)(!toggle);
2734       else
2735         config->nobuffer = toggle;
2736       break;
2737     case 'O': /* --remote-name */
2738       if(subletter == 'a') { /* --remote-name-all */
2739         config->default_node_flags = toggle?GETOUT_USEREMOTE:0;
2740         break;
2741       }
2742     /* fall-through! */
2743     case 'o': /* --output */
2744       /* output file */
2745       {
2746         struct getout *url;
2747         if(config->url_out || (config->url_out=config->url_list)) {
2748           /* there's a node here, if it already is filled-in continue to find
2749              an "empty" node */
2750           while(config->url_out && (config->url_out->flags&GETOUT_OUTFILE))
2751             config->url_out = config->url_out->next;
2752         }
2753
2754         /* now there might or might not be an available node to fill in! */
2755
2756         if(config->url_out)
2757           /* existing node */
2758           url = config->url_out;
2759         else
2760           /* there was no free node, create one! */
2761           url=new_getout(config);
2762
2763         if(url) {
2764           /* fill in the outfile */
2765           if('o' == letter) {
2766             GetStr(&url->outfile, nextarg);
2767             url->flags &= ~GETOUT_USEREMOTE; /* switch off */
2768           }
2769           else {
2770             url->outfile=NULL; /* leave it */
2771             if(toggle)
2772               url->flags |= GETOUT_USEREMOTE;  /* switch on */
2773             else
2774               url->flags &= ~GETOUT_USEREMOTE; /* switch off */
2775           }
2776           url->flags |= GETOUT_OUTFILE;
2777         }
2778       }
2779       break;
2780     case 'P':
2781       /* This makes the FTP sessions use PORT instead of PASV */
2782       /* use <eth0> or <192.168.10.10> style addresses. Anything except
2783          this will make us try to get the "default" address.
2784          NOTE: this is a changed behaviour since the released 4.1!
2785          */
2786       GetStr(&config->ftpport, nextarg);
2787       break;
2788     case 'p':
2789       /* proxy tunnel for non-http protocols */
2790       config->proxytunnel = toggle;
2791       break;
2792
2793     case 'q': /* if used first, already taken care of, we do it like
2794                  this so we don't cause an error! */
2795       break;
2796     case 'Q':
2797       /* QUOTE command to send to FTP server */
2798       switch(nextarg[0]) {
2799       case '-':
2800         /* prefixed with a dash makes it a POST TRANSFER one */
2801         nextarg++;
2802         err = add2list(&config->postquote, nextarg);
2803         break;
2804       case '+':
2805         /* prefixed with a plus makes it a just-before-transfer one */
2806         nextarg++;
2807         err = add2list(&config->prequote, nextarg);
2808         break;
2809       default:
2810         err = add2list(&config->quote, nextarg);
2811         break;
2812       }
2813       if(err)
2814         return err;
2815       break;
2816     case 'r':
2817       /* Specifying a range WITHOUT A DASH will create an illegal HTTP range
2818          (and won't actually be range by definition). The man page previously
2819          claimed that to be a good way, why this code is added to work-around
2820          it. */
2821       if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
2822         char buffer[32];
2823         curl_off_t off;
2824         warnf(config,
2825               "A specified range MUST include at least one dash (-). "
2826               "Appending one for you!\n");
2827         off = curlx_strtoofft(nextarg, NULL, 10);
2828         snprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", off);
2829         GetStr(&config->range, buffer);
2830       }
2831       {
2832         /* byte range requested */
2833         char* tmp_range;
2834         tmp_range=nextarg;
2835         while(*tmp_range != '\0') {
2836           if(!ISDIGIT(*tmp_range)&&*tmp_range!='-'&&*tmp_range!=',') {
2837             warnf(config,"Invalid character is found in given range. "
2838                   "A specified range MUST have only digits in "
2839                   "\'start\'-\'stop\'. The server's response to this "
2840                   "request is uncertain.\n");
2841             break;
2842           }
2843           tmp_range++;
2844         }
2845         /* byte range requested */
2846         GetStr(&config->range, nextarg);
2847       }
2848       break;
2849     case 'R':
2850       /* use remote file's time */
2851       config->remote_time = toggle;
2852       break;
2853     case 's':
2854       /* don't show progress meter, don't show errors : */
2855       if(toggle)
2856         config->mute = config->noprogress = TRUE;
2857       else
2858         config->mute = config->noprogress = FALSE;
2859       config->showerror = (bool)(!toggle); /* toggle off */
2860       break;
2861     case 'S':
2862       /* show errors */
2863       config->showerror = toggle; /* toggle on if used with -s */
2864       break;
2865     case 't':
2866       /* Telnet options */
2867       err = add2list(&config->telnet_options, nextarg);
2868       if(err)
2869         return err;
2870       break;
2871     case 'T':
2872       /* we are uploading */
2873       {
2874         struct getout *url;
2875         if(config->url_out || (config->url_out=config->url_list)) {
2876           /* there's a node here, if it already is filled-in continue to find
2877              an "empty" node */
2878           while(config->url_out && (config->url_out->flags&GETOUT_UPLOAD))
2879             config->url_out = config->url_out->next;
2880         }
2881
2882         /* now there might or might not be an available node to fill in! */
2883
2884         if(config->url_out)
2885           /* existing node */
2886           url = config->url_out;
2887         else
2888           /* there was no free node, create one! */
2889           url=new_getout(config);
2890
2891         if(url) {
2892           url->flags |= GETOUT_UPLOAD; /* mark -T used */
2893           if(!*nextarg)
2894             url->flags |= GETOUT_NOUPLOAD;
2895           else {
2896             /* "-" equals stdin, but keep the string around for now */
2897             GetStr(&url->infile, nextarg);
2898           }
2899         }
2900       }
2901       break;
2902     case 'u':
2903       /* user:password  */
2904       GetStr(&config->userpwd, nextarg);
2905       cleanarg(nextarg);
2906       checkpasswd("host", &config->userpwd);
2907       break;
2908     case 'U':
2909       /* Proxy user:password  */
2910       GetStr(&config->proxyuserpwd, nextarg);
2911       cleanarg(nextarg);
2912       checkpasswd("proxy", &config->proxyuserpwd);
2913       break;
2914     case 'v':
2915       if(toggle) {
2916         /* the '%' thing here will cause the trace get sent to stderr */
2917         GetStr(&config->trace_dump, (char *)"%");
2918         if(config->tracetype && (config->tracetype != TRACE_PLAIN))
2919           warnf(config,
2920                 "-v/--verbose overrides an earlier trace/verbose option\n");
2921         config->tracetype = TRACE_PLAIN;
2922       }
2923       else
2924         /* verbose is disabled here */
2925         config->tracetype = TRACE_NONE;
2926       break;
2927     case 'V':
2928     {
2929       const char * const *proto;
2930
2931       if(!toggle)
2932         /* --no-version yields no output! */
2933         break;
2934
2935       printf(CURL_ID "%s\n", curl_version());
2936       if (curlinfo->protocols) {
2937         printf("Protocols: ");
2938         for (proto=curlinfo->protocols; *proto; ++proto) {
2939           printf("%s ", *proto);
2940         }
2941         puts(""); /* newline */
2942       }
2943       if(curlinfo->features) {
2944         unsigned int i;
2945         struct feat {
2946           const char *name;
2947           int bitmask;
2948         };
2949         static const struct feat feats[] = {
2950           {"AsynchDNS", CURL_VERSION_ASYNCHDNS},
2951           {"Debug", CURL_VERSION_DEBUG},
2952           {"TrackMemory", CURL_VERSION_CURLDEBUG},
2953           {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE},
2954           {"IDN", CURL_VERSION_IDN},
2955           {"IPv6", CURL_VERSION_IPV6},
2956           {"Largefile", CURL_VERSION_LARGEFILE},
2957           {"NTLM", CURL_VERSION_NTLM},
2958           {"SPNEGO", CURL_VERSION_SPNEGO},
2959           {"SSL",  CURL_VERSION_SSL},
2960           {"SSPI",  CURL_VERSION_SSPI},
2961           {"krb4", CURL_VERSION_KERBEROS4},
2962           {"libz", CURL_VERSION_LIBZ},
2963           {"CharConv", CURL_VERSION_CONV}
2964         };
2965         printf("Features: ");
2966         for(i=0; i<sizeof(feats)/sizeof(feats[0]); i++) {
2967           if(curlinfo->features & feats[i].bitmask)
2968             printf("%s ", feats[i].name);
2969         }
2970         puts(""); /* newline */
2971       }
2972     }
2973     return PARAM_HELP_REQUESTED;
2974     case 'w':
2975       /* get the output string */
2976       if('@' == *nextarg) {
2977         /* the data begins with a '@' letter, it means that a file name
2978            or - (stdin) follows */
2979         FILE *file;
2980         const char *fname;
2981         nextarg++; /* pass the @ */
2982         if(curlx_strequal("-", nextarg)) {
2983           fname = "<stdin>";
2984           file = stdin;
2985         }
2986         else {
2987           fname = nextarg;
2988           file = fopen(nextarg, "r");
2989         }
2990         err = file2string(&config->writeout, file);
2991         if(file && (file != stdin))
2992           fclose(file);
2993         if(err)
2994           return err;
2995         if(!config->writeout)
2996           warnf(config, "Failed to read %s", fname);
2997       }
2998       else
2999         GetStr(&config->writeout, nextarg);
3000       break;
3001     case 'x':
3002       /* proxy */
3003       GetStr(&config->proxy, nextarg);
3004       config->proxyver = CURLPROXY_HTTP;
3005       break;
3006     case 'X':
3007       /* set custom request */
3008       GetStr(&config->customrequest, nextarg);
3009       break;
3010     case 'y':
3011       /* low speed time */
3012       if(str2num(&config->low_speed_time, nextarg))
3013         return PARAM_BAD_NUMERIC;
3014       if(!config->low_speed_limit)
3015         config->low_speed_limit = 1;
3016       break;
3017     case 'Y':
3018       /* low speed limit */
3019       if(str2num(&config->low_speed_limit, nextarg))
3020         return PARAM_BAD_NUMERIC;
3021       if(!config->low_speed_time)
3022         config->low_speed_time=30;
3023       break;
3024     case 'z': /* time condition coming up */
3025       switch(*nextarg) {
3026       case '+':
3027         nextarg++;
3028       default:
3029         /* If-Modified-Since: (section 14.28 in RFC2068) */
3030         config->timecond = CURL_TIMECOND_IFMODSINCE;
3031         break;
3032       case '-':
3033         /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
3034         config->timecond = CURL_TIMECOND_IFUNMODSINCE;
3035         nextarg++;
3036         break;
3037       case '=':
3038         /* Last-Modified:  (section 14.29 in RFC2068) */
3039         config->timecond = CURL_TIMECOND_LASTMOD;
3040         nextarg++;
3041         break;
3042       }
3043       now=time(NULL);
3044       config->condtime=curl_getdate(nextarg, &now);
3045       if(-1 == (int)config->condtime) {
3046         /* now let's see if it is a file name to get the time from instead! */
3047         struct_stat statbuf;
3048         if(-1 == stat(nextarg, &statbuf)) {
3049           /* failed, remove time condition */
3050           config->timecond = CURL_TIMECOND_NONE;
3051           warnf(config,
3052                 "Illegal date format for -z/--timecond (and not "
3053                 "a file name). Disabling time condition. "
3054                 "See curl_getdate(3) for valid date syntax.\n");
3055         }
3056         else {
3057           /* pull the time out from the file */
3058           config->condtime = statbuf.st_mtime;
3059         }
3060       }
3061       break;
3062     default: /* unknown flag */
3063       return PARAM_OPTION_UNKNOWN;
3064     }
3065     hit = -1;
3066
3067   } while(!longopt && !singleopt && *++parse && !*usedarg);
3068
3069   return PARAM_OK;
3070 }
3071
3072 /*
3073  * Copies the string from line to the buffer at param, unquoting
3074  * backslash-quoted characters and NUL-terminating the output string.
3075  * Stops at the first non-backslash-quoted double quote character or the
3076  * end of the input string. param must be at least as long as the input
3077  * string.  Returns the pointer after the last handled input character.
3078  */
3079 static const char *unslashquote(const char *line, char *param)
3080 {
3081   while(*line && (*line != '\"')) {
3082     if(*line == '\\') {
3083       char out;
3084       line++;
3085
3086       /* default is to output the letter after the backslash */
3087       switch(out = *line) {
3088       case '\0':
3089         continue; /* this'll break out of the loop */
3090       case 't':
3091         out='\t';
3092         break;
3093       case 'n':
3094         out='\n';
3095         break;
3096       case 'r':
3097         out='\r';
3098         break;
3099       case 'v':
3100         out='\v';
3101         break;
3102       }
3103       *param++=out;
3104       line++;
3105     }
3106     else
3107       *param++=*line++;
3108   }
3109   *param=0; /* always zero terminate */
3110   return line;
3111 }
3112
3113 /* return 0 on everything-is-fine, and non-zero otherwise */
3114 static int parseconfig(const char *filename,
3115                        struct Configurable *config)
3116 {
3117   int res;
3118   FILE *file;
3119   char filebuffer[512];
3120   bool usedarg;
3121   char *home;
3122   int rc = 0;
3123
3124   if(!filename || !*filename) {
3125     /* NULL or no file name attempts to load .curlrc from the homedir! */
3126
3127 #define CURLRC DOT_CHAR "curlrc"
3128
3129 #ifndef __AMIGA__
3130     filename = CURLRC;   /* sensible default */
3131     home = homedir();    /* portable homedir finder */
3132     if(home) {
3133       if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) {
3134         snprintf(filebuffer, sizeof(filebuffer),
3135                  "%s%s%s", home, DIR_CHAR, CURLRC);
3136
3137 #ifdef WIN32
3138         /* Check if the file exists - if not, try CURLRC in the same
3139          * directory as our executable
3140          */
3141         file = fopen(filebuffer, "r");
3142         if (file != NULL) {
3143           fclose(file);
3144           filename = filebuffer;
3145         }
3146         else {
3147           /* Get the filename of our executable. GetModuleFileName is
3148            * already declared via inclusions done in setup header file.
3149            * We assume that we are using the ASCII version here.
3150            */
3151           int n = GetModuleFileName(0, filebuffer, sizeof(filebuffer));
3152           if (n > 0 && n < (int)sizeof(filebuffer)) {
3153             /* We got a valid filename - get the directory part */
3154             char *lastdirchar = strrchr(filebuffer, '\\');
3155             if (lastdirchar) {
3156               size_t remaining;
3157               *lastdirchar = 0;
3158               /* If we have enough space, build the RC filename */
3159               remaining = sizeof(filebuffer) - strlen(filebuffer);
3160               if (strlen(CURLRC) < remaining - 1) {
3161                 snprintf(lastdirchar, remaining,
3162                          "%s%s", DIR_CHAR, CURLRC);
3163                 /* Don't bother checking if it exists - we do
3164                  * that later
3165                  */
3166                 filename = filebuffer;
3167               }
3168             }
3169           }
3170         }
3171 #else /* WIN32 */
3172         filename = filebuffer;
3173 #endif /* WIN32 */
3174       }
3175       free(home); /* we've used it, now free it */
3176     }
3177
3178 # else /* __AMIGA__ */
3179   /* On AmigaOS all the config files are into env:
3180    */
3181   filename = "ENV:" CURLRC;
3182
3183 #endif
3184   }
3185
3186   if(strcmp(filename,"-"))
3187     file = fopen(filename, "r");
3188   else
3189     file = stdin;
3190
3191   if(file) {
3192     char *line;
3193     char *aline;
3194     char *option;
3195     char *param;
3196     int lineno=0;
3197     bool alloced_param;
3198
3199 #define ISSEP(x) (((x)=='=') || ((x) == ':'))
3200
3201     while (NULL != (aline = my_get_line(file))) {
3202       lineno++;
3203       line = aline;
3204       alloced_param=FALSE;
3205
3206       /* line with # in the first non-blank column is a comment! */
3207       while(*line && ISSPACE(*line))
3208         line++;
3209
3210       switch(*line) {
3211       case '#':
3212       case '/':
3213       case '\r':
3214       case '\n':
3215       case '*':
3216       case '\0':
3217         free(aline);
3218         continue;
3219       }
3220
3221       /* the option keywords starts here */
3222       option = line;
3223       while(*line && !ISSPACE(*line) && !ISSEP(*line))
3224         line++;
3225       /* ... and has ended here */
3226
3227       if(*line)
3228         *line++=0; /* zero terminate, we have a local copy of the data */
3229
3230 #ifdef DEBUG_CONFIG
3231       fprintf(stderr, "GOT: %s\n", option);
3232 #endif
3233
3234       /* pass spaces and separator(s) */
3235       while(*line && (ISSPACE(*line) || ISSEP(*line)))
3236         line++;
3237
3238       /* the parameter starts here (unless quoted) */
3239       if(*line == '\"') {
3240         /* quoted parameter, do the quote dance */
3241         line++;
3242         param=malloc(strlen(line)+1); /* parameter */
3243         if (!param) {
3244           /* out of memory */
3245           free(aline);
3246           rc = 1;
3247           break;
3248         }
3249         alloced_param=TRUE;
3250         (void)unslashquote(line, param);
3251       }
3252       else {
3253         param=line; /* parameter starts here */
3254         while(*line && !ISSPACE(*line))
3255           line++;
3256         *line=0; /* zero terminate */
3257       }
3258
3259       if (param && !*param) {
3260         /* do this so getparameter can check for required parameters.
3261            Otherwise it always thinks there's a parameter. */
3262         if (alloced_param)
3263           free(param);
3264         param = NULL;
3265       }
3266
3267 #ifdef DEBUG_CONFIG
3268       fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)"));
3269 #endif
3270       res = getparameter(option, param, &usedarg, config);
3271
3272       if (param && *param && !usedarg)
3273         /* we passed in a parameter that wasn't used! */
3274         res = PARAM_GOT_EXTRA_PARAMETER;
3275
3276       if(res != PARAM_OK) {
3277         /* the help request isn't really an error */
3278         if(!strcmp(filename, "-")) {
3279           filename=(char *)"<stdin>";
3280         }
3281         if(PARAM_HELP_REQUESTED != res) {
3282           const char *reason = param2text(res);
3283           warnf(config, "%s:%d: warning: '%s' %s\n",
3284                 filename, lineno, option, reason);
3285         }
3286       }
3287
3288       if(alloced_param)
3289       {
3290         free(param);
3291         param = NULL;
3292       }
3293
3294       free(aline);
3295     }
3296     if(file != stdin)
3297       fclose(file);
3298   }
3299   else
3300     rc = 1; /* couldn't open the file */
3301   return rc;
3302 }
3303
3304 static void go_sleep(long ms)
3305 {
3306 #ifdef HAVE_POLL_FINE
3307   /* portable subsecond "sleep" */
3308   poll((void *)0, 0, (int)ms);
3309 #else
3310   /* systems without poll() need other solutions */
3311
3312 #ifdef WIN32
3313   /* Windows offers a millisecond sleep */
3314   Sleep(ms);
3315 #elif defined(MSDOS)
3316   delay(ms);
3317 #else
3318   /* Other systems must use select() for this */
3319   struct timeval timeout;
3320
3321   timeout.tv_sec = ms/1000;
3322   ms = ms%1000;
3323   timeout.tv_usec = ms * 1000;
3324
3325   select(0, NULL,  NULL, NULL, &timeout);
3326 #endif
3327
3328 #endif
3329 }
3330
3331 static size_t my_fwrite(void *buffer, size_t sz, size_t nmemb, void *stream)
3332 {
3333   size_t rc;
3334   struct OutStruct *out=(struct OutStruct *)stream;
3335   struct Configurable *config = out->config;
3336
3337   /*
3338    * Once that libcurl has called back my_fwrite() the returned value
3339    * is checked against the amount that was intended to be written, if
3340    * it does not match then it fails with CURLE_WRITE_ERROR. So at this
3341    * point returning a value different from sz*nmemb indicates failure.
3342    */
3343   const size_t err_rc = (sz * nmemb) ? 0 : 1;
3344
3345   if(!out->stream) {
3346     if (!out->filename) {
3347       warnf(config, "Remote filename has no length!\n");
3348       return err_rc; /* Failure */
3349     }
3350
3351     if (config->content_disposition) {
3352       /* don't overwrite existing files */
3353       FILE* f = fopen(out->filename, "r");
3354       if (f) {
3355         fclose(f);
3356         warnf(config, "Refusing to overwrite %s: %s\n", out->filename,
3357               strerror(EEXIST));
3358         return err_rc; /* Failure */
3359       }
3360     }
3361
3362     /* open file for writing */
3363     out->stream=fopen(out->filename, "wb");
3364     if(!out->stream) {
3365       warnf(config, "Failed to create the file %s: %s\n", out->filename,
3366             strerror(errno));
3367       return err_rc; /* failure */
3368     }
3369   }
3370
3371   rc = fwrite(buffer, sz, nmemb, out->stream);
3372
3373   if((sz * nmemb) == rc) {
3374     /* we added this amount of data to the output */
3375     out->bytes += (sz * nmemb);
3376   }
3377
3378   if(config->readbusy) {
3379     config->readbusy = FALSE;
3380     curl_easy_pause(config->easy, CURLPAUSE_CONT);
3381   }
3382
3383   if(config->nobuffer) {
3384     /* disable output buffering */
3385     int res = fflush(out->stream);
3386     if(res) {
3387       /* return a value that isn't the same as sz * nmemb */
3388       return err_rc; /* failure */
3389     }
3390   }
3391
3392   return rc;
3393 }
3394
3395 struct InStruct {
3396   int fd;
3397   struct Configurable *config;
3398 };
3399
3400 #define MAX_SEEK 2147483647
3401
3402 /*
3403  * my_seek() is the CURLOPT_SEEKFUNCTION we use
3404  */
3405 static int my_seek(void *stream, curl_off_t offset, int whence)
3406 {
3407   struct InStruct *in=(struct InStruct *)stream;
3408
3409 #if (CURL_SIZEOF_CURL_OFF_T > SIZEOF_OFF_T) && !defined(USE_WIN32_LARGE_FILES)
3410   /* The offset check following here is only interesting if curl_off_t is
3411      larger than off_t and we are not using the WIN32 large file support
3412      macros that provide the support to do 64bit seeks correctly */
3413
3414   if(offset > MAX_SEEK) {
3415     /* Some precaution code to work around problems with different data sizes
3416        to allow seeking >32bit even if off_t is 32bit. Should be very rare and
3417        is really valid on weirdo-systems. */
3418     curl_off_t left = offset;
3419
3420     if(whence != SEEK_SET)
3421       /* this code path doesn't support other types */
3422       return 1;
3423
3424     if(LSEEK_ERROR == lseek(in->fd, 0, SEEK_SET))
3425       /* couldn't rewind to beginning */
3426       return 1;
3427
3428     while(left) {
3429       long step = (left>MAX_SEEK ? MAX_SEEK : (long)left);
3430       if(LSEEK_ERROR == lseek(in->fd, step, SEEK_CUR))
3431         /* couldn't seek forwards the desired amount */
3432         return 1;
3433       left -= step;
3434     }
3435     return 0;
3436   }
3437 #endif
3438   if(LSEEK_ERROR == lseek(in->fd, offset, whence))
3439     /* couldn't rewind, the reason is in errno but errno is just not portable
3440        enough and we don't actually care that much why we failed. We'll let
3441        libcurl know that it may try other means if it wants to. */
3442     return CURL_SEEKFUNC_CANTSEEK;
3443
3444   return 0;
3445 }
3446
3447 static size_t my_fread(void *buffer, size_t sz, size_t nmemb, void *userp)
3448 {
3449   ssize_t rc;
3450   struct InStruct *in=(struct InStruct *)userp;
3451
3452   rc = read(in->fd, buffer, sz*nmemb);
3453   if(rc < 0) {
3454     if(errno == EAGAIN) {
3455       errno = 0;
3456       in->config->readbusy = TRUE;
3457       return CURL_READFUNC_PAUSE;
3458     }
3459     /* since size_t is unsigned we can't return negative values fine */
3460     rc = 0;
3461   }
3462   in->config->readbusy = FALSE;
3463   return (size_t)rc;
3464 }
3465
3466 struct ProgressData {
3467   int calls;
3468   curl_off_t prev;
3469   int width;
3470   FILE *out; /* where to write everything to */
3471   curl_off_t initial_size;
3472 };
3473
3474 static int myprogress (void *clientp,
3475                        double dltotal,
3476                        double dlnow,
3477                        double ultotal,
3478                        double ulnow)
3479 {
3480   /* The original progress-bar source code was written for curl by Lars Aas,
3481      and this new edition inherits some of his concepts. */
3482
3483   char line[256];
3484   char outline[256];
3485   char format[40];
3486   double frac;
3487   double percent;
3488   int barwidth;
3489   int num;
3490   int i;
3491
3492   struct ProgressData *bar = (struct ProgressData *)clientp;
3493   curl_off_t total = (curl_off_t)dltotal + (curl_off_t)ultotal +
3494     bar->initial_size; /* expected transfer size */
3495   curl_off_t point = (curl_off_t)dlnow + (curl_off_t)ulnow +
3496     bar->initial_size; /* we've come this far */
3497
3498   if(point > total)
3499     /* we have got more than the expected total! */
3500     total = point;
3501
3502   bar->calls++; /* simply count invokes */
3503
3504   if(total < 1) {
3505     curl_off_t prevblock = bar->prev / 1024;
3506     curl_off_t thisblock = point / 1024;
3507     while ( thisblock > prevblock ) {
3508       fprintf( bar->out, "#" );
3509       prevblock++;
3510     }
3511   }
3512   else {
3513     frac = (double)point / (double)total;
3514     percent = frac * 100.0f;
3515     barwidth = bar->width - 7;
3516     num = (int) (((double)barwidth) * frac);
3517     for ( i = 0; i < num; i++ ) {
3518       line[i] = '#';
3519     }
3520     line[i] = '\0';
3521     snprintf( format, sizeof(format), "%%-%ds %%5.1f%%%%", barwidth );
3522     snprintf( outline, sizeof(outline), format, line, percent );
3523     fprintf( bar->out, "\r%s", outline );
3524   }
3525   fflush(bar->out);
3526   bar->prev = point;
3527
3528   return 0;
3529 }
3530
3531 static
3532 void progressbarinit(struct ProgressData *bar,
3533                      struct Configurable *config)
3534 {
3535 #ifdef __EMX__
3536   /* 20000318 mgs */
3537   int scr_size [2];
3538 #endif
3539   char *colp;
3540
3541   memset(bar, 0, sizeof(struct ProgressData));
3542
3543   /* pass this through to progress function so
3544    * it can display progress towards total file
3545    * not just the part that's left. (21-may-03, dbyron) */
3546   if (config->use_resume)
3547     bar->initial_size = config->resume_from;
3548
3549 /* TODO: get terminal width through ansi escapes or something similar.
3550          try to update width when xterm is resized... - 19990617 larsa */
3551 #ifndef __EMX__
3552   /* 20000318 mgs
3553    * OS/2 users most likely won't have this env var set, and besides that
3554    * we're using our own way to determine screen width */
3555   colp = curlx_getenv("COLUMNS");
3556   if (colp != NULL) {
3557     bar->width = atoi(colp);
3558     curl_free(colp);
3559   }
3560   else
3561     bar->width = 79;
3562 #else
3563   /* 20000318 mgs
3564    * We use this emx library call to get the screen width, and subtract
3565    * one from what we got in order to avoid a problem with the cursor
3566    * advancing to the next line if we print a string that is as long as
3567    * the screen is wide. */
3568
3569   _scrsize(scr_size);
3570   bar->width = scr_size[0] - 1;
3571 #endif
3572
3573   bar->out = config->errors;
3574 }
3575
3576
3577 static
3578 void dump(const char *timebuf, const char *text,
3579           FILE *stream, const unsigned char *ptr, size_t size,
3580           trace tracetype, curl_infotype infotype)
3581 {
3582   size_t i;
3583   size_t c;
3584
3585   unsigned int width=0x10;
3586
3587   if(tracetype == TRACE_ASCII)
3588     /* without the hex output, we can fit more on screen */
3589     width = 0x40;
3590
3591   fprintf(stream, "%s%s, %zd bytes (0x%zx)\n", timebuf, text, size, size);
3592
3593   for(i=0; i<size; i+= width) {
3594
3595     fprintf(stream, "%04zx: ", i);
3596
3597     if(tracetype == TRACE_BIN) {
3598       /* hex not disabled, show it */
3599       for(c = 0; c < width; c++)
3600         if(i+c < size)
3601           fprintf(stream, "%02x ", ptr[i+c]);
3602         else
3603           fputs("   ", stream);
3604     }
3605
3606     for(c = 0; (c < width) && (i+c < size); c++) {
3607       /* check for 0D0A; if found, skip past and start a new line of output */
3608       if ((tracetype == TRACE_ASCII) &&
3609           (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
3610         i+=(c+2-width);
3611         break;
3612       }
3613 #ifdef CURL_DOES_CONVERSIONS
3614       /* repeat the 0D0A check above but use the host encoding for CRLF */
3615       if ((tracetype == TRACE_ASCII) &&
3616           (i+c+1 < size) && ptr[i+c]=='\r' && ptr[i+c+1]=='\n') {
3617         i+=(c+2-width);
3618         break;
3619       }
3620       /* convert to host encoding and print this character */
3621       fprintf(stream, "%c", convert_char(infotype, ptr[i+c]));
3622 #else
3623       (void)infotype;
3624       fprintf(stream, "%c",
3625               (ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:UNPRINTABLE_CHAR);
3626 #endif /* CURL_DOES_CONVERSIONS */
3627       /* check again for 0D0A, to avoid an extra \n if it's at width */
3628       if ((tracetype == TRACE_ASCII) &&
3629           (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
3630         i+=(c+3-width);
3631         break;
3632       }
3633     }
3634     fputc('\n', stream); /* newline */
3635   }
3636   fflush(stream);
3637 }
3638
3639 static
3640 int my_trace(CURL *handle, curl_infotype type,
3641              unsigned char *data, size_t size,
3642              void *userp)
3643 {
3644   struct Configurable *config = (struct Configurable *)userp;
3645   FILE *output=config->errors;
3646   const char *text;
3647   struct timeval tv;
3648   struct tm *now;
3649   char timebuf[20];
3650   time_t secs;
3651   static time_t epoch_offset;
3652   static int    known_offset;
3653
3654   (void)handle; /* prevent compiler warning */
3655
3656   if(config->tracetime) {
3657     tv = cutil_tvnow();
3658     if(!known_offset) {
3659       epoch_offset = time(NULL) - tv.tv_sec;
3660       known_offset = 1;
3661     }
3662     secs = epoch_offset + tv.tv_sec;
3663     now = localtime(&secs);  /* not thread safe but we don't care */
3664     snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld ",
3665              now->tm_hour, now->tm_min, now->tm_sec, (long)tv.tv_usec);
3666   }
3667   else
3668     timebuf[0]=0;
3669
3670   if(!config->trace_stream) {
3671     /* open for append */
3672     if(curlx_strequal("-", config->trace_dump))
3673       config->trace_stream = stdout;
3674     else if(curlx_strequal("%", config->trace_dump))
3675       /* Ok, this is somewhat hackish but we do it undocumented for now */
3676       config->trace_stream = config->errors;  /* aka stderr */
3677     else {
3678       config->trace_stream = fopen(config->trace_dump, "w");
3679       config->trace_fopened = TRUE;
3680     }
3681   }
3682
3683   if(config->trace_stream)
3684     output = config->trace_stream;
3685
3686   if(!output) {
3687     warnf(config, "Failed to create/open output");
3688     return 0;
3689   }
3690
3691   if(config->tracetype == TRACE_PLAIN) {
3692     /*
3693      * This is the trace look that is similar to what libcurl makes on its
3694      * own.
3695      */
3696     static const char * const s_infotype[] = {
3697       "*", "<", ">", "{", "}", "{", "}"
3698     };
3699     size_t i;
3700     size_t st=0;
3701     static bool newl = FALSE;
3702     static bool traced_data = FALSE;
3703
3704     switch(type) {
3705     case CURLINFO_HEADER_OUT:
3706       for(i=0; i<size-1; i++) {
3707         if(data[i] == '\n') { /* LF */
3708           if(!newl) {
3709             fprintf(output, "%s%s ", timebuf, s_infotype[type]);
3710           }
3711           (void)fwrite(data+st, i-st+1, 1, output);
3712           st = i+1;
3713           newl = FALSE;
3714         }
3715       }
3716       if(!newl)
3717         fprintf(output, "%s%s ", timebuf, s_infotype[type]);
3718       (void)fwrite(data+st, i-st+1, 1, output);
3719       newl = (bool)(size && (data[size-1] != '\n'));
3720       traced_data = FALSE;
3721       break;
3722     case CURLINFO_TEXT:
3723     case CURLINFO_HEADER_IN:
3724       if(!newl)
3725         fprintf(output, "%s%s ", timebuf, s_infotype[type]);
3726       (void)fwrite(data, size, 1, output);
3727       newl = (bool)(size && (data[size-1] != '\n'));
3728       traced_data = FALSE;
3729       break;
3730     case CURLINFO_DATA_OUT:
3731     case CURLINFO_DATA_IN:
3732     case CURLINFO_SSL_DATA_IN:
3733     case CURLINFO_SSL_DATA_OUT:
3734       if(!traced_data) {
3735         /* if the data is output to a tty and we're sending this debug trace
3736            to stderr or stdout, we don't display the alert about the data not
3737            being shown as the data _is_ shown then just not via this
3738            function */
3739         if(!config->isatty ||
3740            ((output != stderr) && (output != stdout))) {
3741           if(!newl)
3742             fprintf(output, "%s%s ", timebuf, s_infotype[type]);
3743           fprintf(output, "[data not shown]\n");
3744           newl = FALSE;
3745           traced_data = TRUE;
3746         }
3747       }
3748       break;
3749     default: /* nada */
3750       newl = FALSE;
3751       traced_data = FALSE;
3752       break;
3753     }
3754
3755     return 0;
3756   }
3757
3758 #ifdef CURL_DOES_CONVERSIONS
3759   /* Special processing is needed for CURLINFO_HEADER_OUT blocks
3760    * if they contain both headers and data (separated by CRLFCRLF).
3761    * We dump the header text and then switch type to CURLINFO_DATA_OUT.
3762    */
3763   if((type == CURLINFO_HEADER_OUT) && (size > 4)) {
3764     size_t i;
3765     for(i = 0; i < size - 4; i++) {
3766       if(memcmp(&data[i], "\r\n\r\n", 4) == 0) {
3767         /* dump everthing through the CRLFCRLF as a sent header */
3768         text = "=> Send header";
3769         dump(timebuf, text, output, data, i+4, config->tracetype, type);
3770         data += i + 3;
3771         size -= i + 4;
3772         type = CURLINFO_DATA_OUT;
3773         data += 1;
3774         break;
3775       }
3776     }
3777   }
3778 #endif /* CURL_DOES_CONVERSIONS */
3779
3780   switch (type) {
3781   case CURLINFO_TEXT:
3782     fprintf(output, "%s== Info: %s", timebuf, data);
3783   default: /* in case a new one is introduced to shock us */
3784     return 0;
3785
3786   case CURLINFO_HEADER_OUT:
3787     text = "=> Send header";
3788     break;
3789   case CURLINFO_DATA_OUT:
3790     text = "=> Send data";
3791     break;
3792   case CURLINFO_HEADER_IN:
3793     text = "<= Recv header";
3794     break;
3795   case CURLINFO_DATA_IN:
3796     text = "<= Recv data";
3797     break;
3798   case CURLINFO_SSL_DATA_IN:
3799     text = "<= Recv SSL data";
3800     break;
3801   case CURLINFO_SSL_DATA_OUT:
3802     text = "=> Send SSL data";
3803     break;
3804   }
3805
3806   dump(timebuf, text, output, data, size, config->tracetype, type);
3807   return 0;
3808 }
3809
3810 static void free_config_fields(struct Configurable *config)
3811 {
3812   if(config->random_file)
3813     free(config->random_file);
3814   if(config->egd_file)
3815     free(config->egd_file);
3816   if(config->trace_dump)
3817     free(config->trace_dump);
3818   if(config->cipher_list)
3819     free(config->cipher_list);
3820   if(config->userpwd)
3821     free(config->userpwd);
3822   if(config->postfields)
3823     free(config->postfields);
3824   if(config->proxy)
3825     free(config->proxy);
3826   if(config->proxyuserpwd)
3827     free(config->proxyuserpwd);
3828   if(config->noproxy)
3829     free(config->noproxy);
3830   if(config->cookie)
3831     free(config->cookie);
3832   if(config->cookiefile)
3833     free(config->cookiefile);
3834   if(config->krblevel)
3835     free(config->krblevel);
3836   if(config->headerfile)
3837     free(config->headerfile);
3838   if(config->ftpport)
3839     free(config->ftpport);
3840   if(config->range)
3841     free(config->range);
3842   if(config->customrequest)
3843     free(config->customrequest);
3844   if(config->writeout)
3845     free(config->writeout);
3846   if(config->httppost)
3847     curl_formfree(config->httppost);
3848   if (config->cert)
3849     free(config->cert);
3850   if(config->cacert)
3851     free(config->cacert);
3852   if (config->cert_type)
3853     free(config->cert_type);
3854   if(config->capath)
3855     free(config->capath);
3856   if(config->crlfile)
3857     free(config->crlfile);
3858   if(config->cookiejar)
3859     free(config->cookiejar);
3860   if(config->ftp_account)
3861     free(config->ftp_account);
3862   if(config->ftp_alternative_to_user)
3863     free(config->ftp_alternative_to_user);
3864   if(config->iface)
3865     free(config->iface);
3866   if(config->socksproxy)
3867     free(config->socksproxy);
3868   if(config->libcurl)
3869     free(config->libcurl);
3870   if (config->key_passwd)
3871     free(config->key_passwd);
3872   if (config->key)
3873     free(config->key);
3874   if (config->key_type)
3875     free(config->key_type);
3876   if (config->pubkey)
3877     free(config->pubkey);
3878   if (config->referer)
3879     free(config->referer);
3880   if (config->hostpubmd5)
3881     free(config->hostpubmd5);
3882   if(config->mail_from)
3883     free(config->mail_from);
3884 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
3885   if(config->socks5_gssapi_service)
3886     free(config->socks5_gssapi_service);
3887 #endif
3888
3889   curl_slist_free_all(config->quote); /* checks for config->quote == NULL */
3890   curl_slist_free_all(config->prequote);
3891   curl_slist_free_all(config->postquote);
3892   curl_slist_free_all(config->headers);
3893   curl_slist_free_all(config->telnet_options);
3894   curl_slist_free_all(config->mail_rcpt);
3895
3896   if(config->easy)
3897     curl_easy_cleanup(config->easy);
3898 }
3899
3900 #ifdef WIN32
3901
3902 /* Function to find CACert bundle on a Win32 platform using SearchPath.
3903  * (SearchPath is already declared via inclusions done in setup header file)
3904  * (Use the ASCII version instead of the unicode one!)
3905  * The order of the directories it searches is:
3906  *  1. application's directory
3907  *  2. current working directory
3908  *  3. Windows System directory (e.g. C:\windows\system32)
3909  *  4. Windows Directory (e.g. C:\windows)
3910  *  5. all directories along %PATH%
3911  */
3912 static void FindWin32CACert(struct Configurable *config,
3913                             const char *bundle_file)
3914 {
3915   /* only check for cert file if "we" support SSL */
3916   if(curlinfo->features & CURL_VERSION_SSL) {
3917     DWORD buflen;
3918     char *ptr = NULL;
3919     char *retval = malloc(sizeof (TCHAR) * (MAX_PATH + 1));
3920     if (!retval)
3921       return;
3922     retval[0] = '\0';
3923     buflen = SearchPathA(NULL, bundle_file, NULL, MAX_PATH+2, retval, &ptr);
3924     if (buflen > 0) {
3925       GetStr(&config->cacert, retval);
3926     }
3927     free(retval);
3928   }
3929 }
3930
3931 #endif
3932
3933 #define RETRY_SLEEP_DEFAULT 1000  /* ms */
3934 #define RETRY_SLEEP_MAX     600000 /* ms == 10 minutes */
3935
3936 static bool
3937 output_expected(const char* url, const char* uploadfile)
3938 {
3939   if(!uploadfile)
3940     return TRUE;  /* download */
3941   if(checkprefix("http://", url) || checkprefix("https://", url))
3942     return TRUE;   /* HTTP(S) upload */
3943
3944   return FALSE; /* non-HTTP upload, probably no output should be expected */
3945 }
3946
3947 #define my_setopt(x,y,z) _my_setopt(x, 0, config, #y, y, z)
3948 #define my_setopt_str(x,y,z) _my_setopt(x, 1, config, #y, y, z)
3949
3950 static struct curl_slist *easycode;
3951
3952 static CURLcode _my_setopt(CURL *curl, bool str, struct Configurable *config,
3953                            const char *name, CURLoption tag, ...);
3954
3955 static CURLcode _my_setopt(CURL *curl, bool str, struct Configurable *config,
3956                            const char *name, CURLoption tag, ...)
3957 {
3958   va_list arg;
3959   CURLcode ret;
3960   char *bufp;
3961   char value[256];
3962   bool remark=FALSE;
3963
3964   va_start(arg, tag);
3965
3966   if(tag < CURLOPTTYPE_OBJECTPOINT) {
3967     long lval = va_arg(arg, long);
3968     snprintf(value, sizeof(value), "%ld", lval);
3969     ret = curl_easy_setopt(curl, tag, lval);
3970
3971   }
3972   else if(tag < CURLOPTTYPE_OFF_T) {
3973     void *pval = va_arg(arg, void *);
3974     unsigned char *ptr = (unsigned char *)pval;
3975
3976     /* function pointers are never printable */
3977     if (tag >= CURLOPTTYPE_FUNCTIONPOINT) {
3978       if (pval) {
3979         snprintf(value, sizeof(value), "%p", pval);
3980         remark = TRUE;
3981       }
3982       else
3983         strcpy(value, "NULL");
3984     }
3985
3986     else if(pval && str)
3987       snprintf(value, sizeof(value), "\"%s\"", (char *)ptr);
3988     else if(pval) {
3989       snprintf(value, sizeof(value), "%p", pval);
3990       remark = TRUE;
3991     }
3992     else {
3993       strcpy(value, "NULL"); /* value fits more than 5 bytes */
3994     }
3995     ret = curl_easy_setopt(curl, tag, pval);
3996
3997   }
3998   else {
3999     curl_off_t oval = va_arg(arg, curl_off_t);
4000     snprintf(value, sizeof(value), "%" CURL_FORMAT_CURL_OFF_T, oval);
4001     ret = curl_easy_setopt(curl, tag, oval);
4002   }
4003
4004   if(config->libcurl) {
4005     /* we only use this for real if --libcurl was used */
4006
4007     bufp = curlx_maprintf("%scurl_easy_setopt(hnd, %s, %s);%s",
4008                           remark?"/* ":"", name, value,
4009                           remark?" [REMARK] */":"");
4010
4011     if (!bufp || !curl_slist_append(easycode, bufp))
4012       ret = CURLE_OUT_OF_MEMORY;
4013     if (bufp)
4014       curl_free(bufp);
4015   }
4016   va_end(arg);
4017
4018   return ret;
4019 }
4020
4021 static const char * const srchead[]={
4022   "/********* Sample code generated by the curl command line tool **********",
4023   " * Lines with [REMARK] below might need to be modified to make this code ",
4024   " * usable. Add error code checking where appropriate.",
4025   " * Compile this with a suitable header include path. Then link with ",
4026   " * libcurl.",
4027   " * If you use any *_LARGE options, make sure your compiler figure",
4028   " * out the correct size for the curl_off_t variable.",
4029   " * Read the details for all curl_easy_setopt() options online on:",
4030   " * http://curlm.haxx.se/libcurl/c/curl_easy_setopt.html",
4031   " ************************************************************************/",
4032   "[m]",
4033   "#include <curl/curl.h>",
4034   "",
4035   "int main(int argc, char *argv[])",
4036   "{",
4037   "  CURLcode ret;",
4038   NULL
4039 };
4040
4041 static void dumpeasycode(struct Configurable *config)
4042 {
4043   struct curl_slist *ptr = easycode;
4044   char *o = config->libcurl;
4045
4046   if(o) {
4047     FILE *out;
4048     bool fopened = FALSE;
4049     if(strcmp(o, "-")) {
4050       out = fopen(o, "wt");
4051       fopened = TRUE;
4052     }
4053     else
4054       out= stdout;
4055     if(!out)
4056       warnf(config, "Failed to open %s to write libcurl code!\n", o);
4057     else {
4058       int i;
4059       const char *c;
4060
4061       for(i=0; (c = srchead[i]); i++) {
4062         if(!memcmp((char *)c, "[m]", 3)) {
4063 #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS > 32)
4064           fprintf(out, "#define _FILE_OFFSET_BITS %d "
4065                   "/* for pre libcurl 7.19.0 curl_off_t magic */\n",
4066                   _FILE_OFFSET_BITS);
4067 #endif
4068         }
4069         else
4070           fprintf(out, "%s\n", c);
4071       }
4072
4073       while(ptr) {
4074         fprintf(out, "  %s\n", ptr->data);
4075         ptr = ptr->next;
4076       }
4077       fprintf(out,
4078               "  return (int)ret;\n"
4079               "}\n"
4080               "/**** End of sample code ****/\n");
4081       if(fopened)
4082         fclose(out);
4083     }
4084   }
4085   curl_slist_free_all(easycode);
4086 }
4087
4088 static bool stdin_upload(const char *uploadfile)
4089 {
4090   return (bool)(curlx_strequal(uploadfile, "-") ||
4091                 curlx_strequal(uploadfile, "."));
4092 }
4093
4094 /* Adds the file name to the URL if it doesn't already have one.
4095  * url will be freed before return if the returned pointer is different
4096  */
4097 static char *add_file_name_to_url(CURL *curl, char *url, const char *filename)
4098 {
4099   /* If no file name part is given in the URL, we add this file name */
4100   char *ptr=strstr(url, "://");
4101   if(ptr)
4102     ptr+=3;
4103   else
4104     ptr=url;
4105   ptr = strrchr(ptr, '/');
4106   if(!ptr || !strlen(++ptr)) {
4107     /* The URL has no file name part, add the local file name. In order
4108        to be able to do so, we have to create a new URL in another
4109        buffer.*/
4110
4111     /* We only want the part of the local path that is on the right
4112        side of the rightmost slash and backslash. */
4113     const char *filep = strrchr(filename, '/');
4114     char *file2 = strrchr(filep?filep:filename, '\\');
4115     char *encfile;
4116
4117     if(file2)
4118       filep = file2+1;
4119     else if(filep)
4120       filep++;
4121     else
4122       filep = filename;
4123
4124     /* URL encode the file name */
4125     encfile = curl_easy_escape(curl, filep, 0 /* use strlen */);
4126     if(encfile) {
4127       char *urlbuffer = malloc(strlen(url) + strlen(encfile) + 3);
4128       if(!urlbuffer) {
4129         free(url);
4130         return NULL;
4131       }
4132       if(ptr)
4133         /* there is a trailing slash on the URL */
4134         sprintf(urlbuffer, "%s%s", url, encfile);
4135       else
4136         /* there is no trailing slash on the URL */
4137         sprintf(urlbuffer, "%s/%s", url, encfile);
4138
4139       curl_free(encfile);
4140
4141       free(url);
4142       url = urlbuffer; /* use our new URL instead! */
4143     }
4144   }
4145   return url;
4146 }
4147
4148 /* Extracts the name portion of the URL.
4149  * Returns a heap-allocated string, or NULL if no name part
4150  */
4151 static char *get_url_file_name(const char *url)
4152 {
4153   char *fn = NULL;
4154
4155   /* Find and get the remote file name */
4156   const char * pc =strstr(url, "://");
4157   if(pc)
4158     pc+=3;
4159   else
4160     pc=url;
4161   pc = strrchr(pc, '/');
4162
4163   if(pc) {
4164     /* duplicate the string beyond the slash */
4165     pc++;
4166     fn = *pc ? strdup(pc): NULL;
4167   }
4168   return fn;
4169 }
4170
4171 static char*
4172 parse_filename(char *ptr, size_t len)
4173 {
4174   char* copy;
4175   char* p;
4176   char* q;
4177   char quote = 0;
4178
4179   /* simple implementation of strndup() */
4180   copy = malloc(len+1);
4181   if (!copy)
4182     return NULL;
4183   strncpy(copy, ptr, len);
4184   copy[len] = 0;
4185
4186   p = copy;
4187   if (*p == '\'' || *p == '"') {
4188     /* store the starting quote */
4189     quote = *p;
4190     p++;
4191   }
4192
4193   /* if the filename contains a path, only use filename portion */
4194   q = strrchr(copy, '/');
4195   if (q) {
4196     p=q+1;
4197     if (!*p) {
4198       free(copy);
4199       return NULL;
4200     }
4201   }
4202
4203   if(quote) {
4204     /* if the file name started with a quote, then scan for the end quote and
4205        stop there */
4206     q = strrchr(p, quote);
4207     if (q)
4208       *q = 0;
4209   }
4210   else
4211     q = NULL; /* no start quote, so no end has been found */
4212
4213   if(!q) {
4214     /* make sure the file name doesn't end in \r or \n */
4215     q = strchr(p, '\r');
4216     if(q)
4217       *q  = 0;
4218
4219     q = strchr(p, '\n');
4220     if(q)
4221       *q  = 0;
4222   }
4223
4224   if (copy!=p)
4225     memmove(copy, p, strlen(p)+1);
4226
4227   return copy;
4228 }
4229
4230 static size_t
4231 header_callback(void *ptr, size_t size, size_t nmemb, void *stream)
4232 {
4233   struct OutStruct* outs = (struct OutStruct*)stream;
4234   const char* str = (char*)ptr;
4235   const size_t cb = size*nmemb;
4236   const char* end = (char*)ptr + cb;
4237
4238   if (cb > 20 && curlx_strnequal(str, "Content-disposition:", 20)) {
4239     char *p = (char*)str + 20;
4240
4241     /* look for the 'filename=' parameter
4242        (encoded filenames (*=) are not supported) */
4243     for(;;) {
4244       char *filename;
4245
4246       while (*p && (p < end) && !ISALPHA(*p))
4247         p++;
4248       if (p > end-9)
4249         break;
4250
4251       if (memcmp(p, "filename=", 9)) {
4252         /* no match, find next parameter */
4253         while ((p < end) && (*p != ';'))
4254           p++;
4255         continue;
4256       }
4257       p+=9;
4258       filename = parse_filename(p, cb - (p - str));
4259       if (filename) {
4260         outs->filename = filename;
4261         break;
4262       }
4263     }
4264   }
4265
4266   return cb;
4267 }
4268
4269 static int
4270 operate(struct Configurable *config, int argc, argv_item_t argv[])
4271 {
4272   char errorbuffer[CURL_ERROR_SIZE];
4273   char useragent[256]; /* buah, we don't want a larger default user agent */
4274   struct ProgressData progressbar;
4275   struct getout *urlnode;
4276   struct getout *nextnode;
4277
4278   struct OutStruct outs;
4279   struct OutStruct heads;
4280   struct InStruct input;
4281
4282   URLGlob *urls=NULL;
4283   URLGlob *inglob=NULL;
4284   int urlnum;
4285   int infilenum;
4286   char *uploadfile=NULL; /* a single file, never a glob */
4287
4288   curl_off_t uploadfilesize; /* -1 means unknown */
4289   bool stillflags=TRUE;
4290
4291   bool allocuseragent=FALSE;
4292
4293   char *httpgetfields=NULL;
4294
4295   CURL *curl;
4296   int res = 0;
4297   int i;
4298   long retry_sleep_default;
4299   long retry_sleep;
4300
4301   char *env;
4302
4303   memset(&heads, 0, sizeof(struct OutStruct));
4304
4305 #ifdef CURLDEBUG
4306   /* this sends all memory debug messages to a logfile named memdump */
4307   env = curlx_getenv("CURL_MEMDEBUG");
4308   if(env) {
4309     /* use the value as file name */
4310     char *s = strdup(env);
4311     curl_free(env);
4312     curl_memdebug(s);
4313     free(s);
4314     /* this weird strdup() and stuff here is to make the curl_free() get
4315        called before the memdebug() as otherwise the memdebug tracing will
4316        with tracing a free() without an alloc! */
4317   }
4318   env = curlx_getenv("CURL_MEMLIMIT");
4319   if(env) {
4320     curl_memlimit(atoi(env));
4321     curl_free(env);
4322   }
4323 #endif
4324
4325   /* Initialize curl library - do not call any libcurl functions before.
4326      Note that the CURLDEBUG magic above is an exception, but then that's not
4327      part of the official public API.
4328    */
4329   if (main_init() != CURLE_OK) {
4330     helpf(config->errors, "error initializing curl library\n");
4331     return CURLE_FAILED_INIT;
4332   }
4333
4334   /*
4335    * Get a curl handle to use for all forthcoming curl transfers.  Cleanup
4336    * when all transfers are done.
4337    */
4338   curl = curl_easy_init();
4339   if(!curl) {
4340     clean_getout(config);
4341     return CURLE_FAILED_INIT;
4342   }
4343   config->easy = curl;
4344
4345   memset(&outs,0,sizeof(outs));
4346
4347   config->outs = &outs;
4348
4349   /* we get libcurl info right away */
4350   curlinfo = curl_version_info(CURLVERSION_NOW);
4351
4352   errorbuffer[0]=0; /* prevent junk from being output */
4353
4354   /* setup proper locale from environment */
4355 #ifdef HAVE_SETLOCALE
4356   setlocale(LC_ALL, "");
4357 #endif
4358
4359   /* inits */
4360   config->postfieldsize = -1;
4361   config->showerror=TRUE;
4362   config->use_httpget=FALSE;
4363   config->create_dirs=FALSE;
4364   config->maxredirs = DEFAULT_MAXREDIRS;
4365
4366   if(argc>1 &&
4367      (!curlx_strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
4368      strchr(argv[1], 'q')) {
4369     /*
4370      * The first flag, that is not a verbose name, but a shortname
4371      * and it includes the 'q' flag!
4372      */
4373     ;
4374   }
4375   else {
4376     parseconfig(NULL, config); /* ignore possible failure */
4377   }
4378
4379   if ((argc < 2)  && !config->url_list) {
4380     helpf(config->errors, NULL);
4381     return CURLE_FAILED_INIT;
4382   }
4383
4384   /* Parse options */
4385   for (i = 1; i < argc; i++) {
4386     if(stillflags &&
4387        ('-' == argv[i][0])) {
4388       char *nextarg;
4389       bool passarg;
4390       char *origopt=argv[i];
4391
4392       char *flag = argv[i];
4393
4394       if(curlx_strequal("--", argv[i]))
4395         /* this indicates the end of the flags and thus enables the
4396            following (URL) argument to start with -. */
4397         stillflags=FALSE;
4398       else {
4399         nextarg= (i < argc - 1)? argv[i+1]: NULL;
4400
4401         res = getparameter(flag, nextarg, &passarg, config);
4402         if(res) {
4403           int retval = CURLE_OK;
4404           if(res != PARAM_HELP_REQUESTED) {
4405             const char *reason = param2text(res);
4406             helpf(config->errors, "option %s: %s\n", origopt, reason);
4407             retval = CURLE_FAILED_INIT;
4408           }
4409           clean_getout(config);
4410           return retval;
4411         }
4412
4413         if(passarg) /* we're supposed to skip this */
4414           i++;
4415       }
4416     }
4417     else {
4418       bool used;
4419       /* just add the URL please */
4420       res = getparameter((char *)"--url", argv[i], &used, config);
4421       if(res)
4422         return res;
4423     }
4424   }
4425
4426   retry_sleep_default = config->retry_delay?
4427     config->retry_delay*1000:RETRY_SLEEP_DEFAULT; /* ms */
4428   retry_sleep = retry_sleep_default;
4429
4430   if((!config->url_list || !config->url_list->url) && !config->list_engines) {
4431     clean_getout(config);
4432     helpf(config->errors, "no URL specified!\n");
4433     return CURLE_FAILED_INIT;
4434   }
4435   if(NULL == config->useragent) {
4436     /* set non-zero default values: */
4437     snprintf(useragent, sizeof(useragent),
4438              CURL_NAME "/" CURL_VERSION " (" OS ") " "%s", curl_version());
4439     config->useragent= useragent;
4440   }
4441   else
4442     allocuseragent = TRUE;
4443
4444   /* On WIN32 we can't set the path to curl-ca-bundle.crt
4445    * at compile time. So we look here for the file in two ways:
4446    * 1: look at the environment variable CURL_CA_BUNDLE for a path
4447    * 2: if #1 isn't found, use the windows API function SearchPath()
4448    *    to find it along the app's path (includes app's dir and CWD)
4449    *
4450    * We support the environment variable thing for non-Windows platforms
4451    * too. Just for the sake of it.
4452    */
4453   if (!config->cacert &&
4454       !config->capath &&
4455       !config->insecure_ok) {
4456     env = curlx_getenv("CURL_CA_BUNDLE");
4457     if(env)
4458       GetStr(&config->cacert, env);
4459     else {
4460       env = curlx_getenv("SSL_CERT_DIR");
4461       if(env)
4462         GetStr(&config->capath, env);
4463       else {
4464         env = curlx_getenv("SSL_CERT_FILE");
4465         if(env)
4466           GetStr(&config->cacert, env);
4467       }
4468     }
4469
4470     if(env)
4471       curl_free(env);
4472 #ifdef WIN32
4473     else
4474       FindWin32CACert(config, "curl-ca-bundle.crt");
4475 #endif
4476   }
4477
4478   if (config->postfields) {
4479     if (config->use_httpget) {
4480       /* Use the postfields data for a http get */
4481       httpgetfields = strdup(config->postfields);
4482       free(config->postfields);
4483       config->postfields = NULL;
4484       if(SetHTTPrequest(config,
4485                         (config->no_body?HTTPREQ_HEAD:HTTPREQ_GET),
4486                         &config->httpreq)) {
4487         free(httpgetfields);
4488         return PARAM_BAD_USE;
4489       }
4490     }
4491     else {
4492       if(SetHTTPrequest(config, HTTPREQ_SIMPLEPOST, &config->httpreq))
4493         return PARAM_BAD_USE;
4494     }
4495   }
4496
4497   /* This is the first entry added to easycode and it initializes the slist */
4498   easycode = curl_slist_append(easycode, "CURL *hnd = curl_easy_init();");
4499   if(!easycode) {
4500     clean_getout(config);
4501     res = CURLE_OUT_OF_MEMORY;
4502     goto quit_curl;
4503   }
4504
4505   if (config->list_engines) {
4506     struct curl_slist *engines = NULL;
4507
4508     curl_easy_getinfo(curl, CURLINFO_SSL_ENGINES, &engines);
4509     list_engines(engines);
4510     curl_slist_free_all(engines);
4511     res = CURLE_OK;
4512     goto quit_curl;
4513   }
4514
4515   /* After this point, we should call curl_easy_cleanup() if we decide to bail
4516    * out from this function! */
4517
4518   urlnode = config->url_list;
4519
4520   if(config->headerfile) {
4521     /* open file for output: */
4522     if(strcmp(config->headerfile,"-")) {
4523       heads.filename = config->headerfile;
4524     }
4525     else
4526       heads.stream=stdout;
4527     heads.config = config;
4528   }
4529
4530   /* loop through the list of given URLs */
4531   while(urlnode) {
4532     int up; /* upload file counter within a single upload glob */
4533     char *dourl;
4534     char *url;
4535     char *infiles; /* might be a glob pattern */
4536     char *outfiles=NULL;
4537
4538     /* get the full URL (it might be NULL) */
4539     dourl=urlnode->url;
4540
4541     url = dourl;
4542
4543     if(NULL == url) {
4544       /* This node had no URL, skip it and continue to the next */
4545       if(urlnode->outfile)
4546         free(urlnode->outfile);
4547
4548       /* move on to the next URL */
4549       nextnode=urlnode->next;
4550       free(urlnode); /* free the node */
4551       urlnode = nextnode;
4552       continue; /* next please */
4553     }
4554
4555     /* default output stream is stdout */
4556     outs.stream = stdout;
4557     outs.config = config;
4558     outs.bytes = 0; /* nothing written yet */
4559
4560     /* save outfile pattern before expansion */
4561     if (urlnode->outfile) {
4562       outfiles = strdup(urlnode->outfile);
4563       if (!outfiles) {
4564         clean_getout(config);
4565         break;
4566       }
4567     }
4568
4569     infiles = urlnode->infile;
4570
4571     if(!config->globoff && infiles) {
4572       /* Unless explicitly shut off */
4573       res = glob_url(&inglob, infiles, &infilenum,
4574                      config->showerror?config->errors:NULL);
4575       if(res != CURLE_OK) {
4576         clean_getout(config);
4577         if(outfiles)
4578           free(outfiles);
4579         break;
4580       }
4581     }
4582
4583     /* Here's the loop for uploading multiple files within the same
4584        single globbed string. If no upload, we enter the loop once anyway. */
4585     for(up = 0;
4586         (!up && !infiles) ||
4587           (uploadfile = inglob?
4588            glob_next_url(inglob):
4589            (!up?strdup(infiles):NULL));
4590         up++) {
4591       int separator = 0;
4592       long retry_numretries;
4593       uploadfilesize=-1;
4594
4595       if(!config->globoff) {
4596         /* Unless explicitly shut off, we expand '{...}' and '[...]'
4597            expressions and return total number of URLs in pattern set */
4598         res = glob_url(&urls, dourl, &urlnum,
4599                        config->showerror?config->errors:NULL);
4600         if(res != CURLE_OK) {
4601           break;
4602         }
4603       }
4604       else
4605         urlnum = 1; /* without globbing, this is a single URL */
4606
4607       /* if multiple files extracted to stdout, insert separators! */
4608       separator= ((!outfiles || curlx_strequal(outfiles, "-")) && urlnum > 1);
4609
4610       /* Here's looping around each globbed URL */
4611       for(i = 0;
4612           (url = urls?glob_next_url(urls):(i?NULL:strdup(url)));
4613           i++) {
4614         /* NOTE: In the condition expression in the for() statement above, the
4615            'url' variable is only ever strdup()ed if (i == 0) and thus never
4616            when this loops later on. Further down in this function we call
4617            free(url) and then the code loops. Static code parsers may thus get
4618            tricked into believing that we have a potential access-after-free
4619            here.  I can however not spot any such case. */
4620
4621         int infd = STDIN_FILENO;
4622         bool infdopen;
4623         char *outfile;
4624         struct timeval retrystart;
4625         outfile = outfiles?strdup(outfiles):NULL;
4626
4627         if((urlnode->flags&GETOUT_USEREMOTE) ||
4628            (outfile && !curlx_strequal("-", outfile)) ) {
4629
4630           /*
4631            * We have specified a file name to store the result in, or we have
4632            * decided we want to use the remote file name.
4633            */
4634
4635           if(!outfile) {
4636             /* extract the file name from the URL */
4637             outfile = get_url_file_name(url);
4638             if((!outfile || !*outfile) && !config->content_disposition) {
4639               helpf(config->errors, "Remote file name has no length!\n");
4640               res = CURLE_WRITE_ERROR;
4641               free(url);
4642               break;
4643             }
4644 #if defined(MSDOS) || defined(WIN32)
4645             /* For DOS and WIN32, we do some major replacing of
4646              bad characters in the file name before using it */
4647             outfile = sanitize_dos_name(outfile);
4648             if(!outfile) {
4649               res = CURLE_OUT_OF_MEMORY;
4650               break;
4651             }
4652 #endif /* MSDOS || WIN32 */
4653           }
4654           else if(urls) {
4655             /* fill '#1' ... '#9' terms from URL pattern */
4656             char *storefile = outfile;
4657             outfile = glob_match_url(storefile, urls);
4658             free(storefile);
4659             if(!outfile) {
4660               /* bad globbing */
4661               warnf(config, "bad output glob!\n");
4662               free(url);
4663               res = CURLE_FAILED_INIT;
4664               break;
4665             }
4666           }
4667
4668           /* Create the directory hierarchy, if not pre-existant to a multiple
4669              file output call */
4670
4671           if(config->create_dirs &&
4672              (-1 == create_dir_hierarchy(outfile, config->errors))) {
4673             free(url);
4674             res = CURLE_WRITE_ERROR;
4675             break;
4676           }
4677
4678           if(config->resume_from_current) {
4679             /* We're told to continue from where we are now. Get the
4680                size of the file as it is now and open it for append instead */
4681
4682             struct_stat fileinfo;
4683
4684             /* VMS -- Danger, the filesize is only valid for stream files */
4685             if(0 == stat(outfile, &fileinfo))
4686               /* set offset to current file size: */
4687               config->resume_from = fileinfo.st_size;
4688             else
4689               /* let offset be 0 */
4690               config->resume_from = 0;
4691           }
4692
4693           outs.filename = outfile;
4694
4695           if(config->resume_from) {
4696             outs.init = config->resume_from;
4697             /* open file for output: */
4698             outs.stream=(FILE *) fopen(outfile, config->resume_from?"ab":"wb");
4699             if (!outs.stream) {
4700               helpf(config->errors, "Can't open '%s'!\n", outfile);
4701               free(url);
4702               res = CURLE_WRITE_ERROR;
4703               break;
4704             }
4705           }
4706           else {
4707             outs.stream = NULL; /* open when needed */
4708           }
4709         }
4710         infdopen=FALSE;
4711         if(uploadfile && !stdin_upload(uploadfile)) {
4712           /*
4713            * We have specified a file to upload and it isn't "-".
4714            */
4715           struct_stat fileinfo;
4716
4717           url = add_file_name_to_url(curl, url, uploadfile);
4718           if(!url) {
4719             helpf(config->errors, "out of memory\n");
4720             res = CURLE_OUT_OF_MEMORY;
4721             break;
4722           }
4723           /* VMS Note:
4724            *
4725            * Reading binary from files can be a problem...  Only FIXED, VAR
4726            * etc WITHOUT implied CC will work Others need a \n appended to a
4727            * line
4728            *
4729            * - Stat gives a size but this is UNRELIABLE in VMS As a f.e. a
4730            * fixed file with implied CC needs to have a byte added for every
4731            * record processed, this can by derived from Filesize & recordsize
4732            * for VARiable record files the records need to be counted!  for
4733            * every record add 1 for linefeed and subtract 2 for the record
4734            * header for VARIABLE header files only the bare record data needs
4735            * to be considered with one appended if implied CC
4736            */
4737
4738           infd= open(uploadfile, O_RDONLY | O_BINARY);
4739           if ((infd == -1) || fstat(infd, &fileinfo)) {
4740             helpf(config->errors, "Can't open '%s'!\n", uploadfile);
4741             if(infd != -1)
4742               close(infd);
4743
4744             /* Free the list of remaining URLs and globbed upload files
4745              * to force curl to exit immediately
4746              */
4747             if(urls) {
4748               glob_cleanup(urls);
4749               urls = NULL;
4750             }
4751             if(inglob) {
4752               glob_cleanup(inglob);
4753               inglob = NULL;
4754             }
4755
4756             res = CURLE_READ_ERROR;
4757             goto quit_urls;
4758           }
4759           infdopen=TRUE;
4760           uploadfilesize=fileinfo.st_size;
4761
4762         }
4763         else if(uploadfile && stdin_upload(uploadfile)) {
4764           SET_BINMODE(stdin);
4765           infd = STDIN_FILENO;
4766           if (curlx_strequal(uploadfile, ".")) {
4767             if (curlx_nonblock((curl_socket_t)infd, TRUE) < 0)
4768               warnf(config,
4769                     "fcntl failed on fd=%d: %s\n", infd, strerror(errno));
4770           }
4771         }
4772
4773         if(uploadfile && config->resume_from_current)
4774           config->resume_from = -1; /* -1 will then force get-it-yourself */
4775
4776         if(output_expected(url, uploadfile)
4777            && outs.stream && isatty(fileno(outs.stream)))
4778           /* we send the output to a tty, therefore we switch off the progress
4779              meter */
4780           config->noprogress = config->isatty = TRUE;
4781
4782         if (urlnum > 1 && !(config->mute)) {
4783           fprintf(config->errors, "\n[%d/%d]: %s --> %s\n",
4784                   i+1, urlnum, url, outfile ? outfile : "<stdout>");
4785           if (separator)
4786             printf("%s%s\n", CURLseparator, url);
4787         }
4788         if (httpgetfields) {
4789           char *urlbuffer;
4790           /* Find out whether the url contains a file name */
4791           const char *pc =strstr(url, "://");
4792           char sep='?';
4793           if(pc)
4794             pc+=3;
4795           else
4796             pc=url;
4797
4798           pc = strrchr(pc, '/'); /* check for a slash */
4799
4800           if(pc) {
4801             /* there is a slash present in the URL */
4802
4803             if(strchr(pc, '?'))
4804               /* Ouch, there's already a question mark in the URL string, we
4805                  then append the data with an ampersand separator instead! */
4806               sep='&';
4807           }
4808           /*
4809            * Then append ? followed by the get fields to the url.
4810            */
4811           urlbuffer = malloc(strlen(url) + strlen(httpgetfields) + 3);
4812           if(!urlbuffer) {
4813             helpf(config->errors, "out of memory\n");
4814
4815             /* Free the list of remaining URLs and globbed upload files
4816              * to force curl to exit immediately
4817              */
4818             if(urls) {
4819               glob_cleanup(urls);
4820               urls = NULL;
4821             }
4822             if(inglob) {
4823               glob_cleanup(inglob);
4824               inglob = NULL;
4825             }
4826
4827             res = CURLE_OUT_OF_MEMORY;
4828             goto quit_urls;
4829           }
4830           if (pc)
4831             sprintf(urlbuffer, "%s%c%s", url, sep, httpgetfields);
4832           else
4833             /* Append  / before the ? to create a well-formed url
4834                if the url contains a hostname only
4835             */
4836             sprintf(urlbuffer, "%s/?%s", url, httpgetfields);
4837
4838           free(url); /* free previous URL */
4839           url = urlbuffer; /* use our new URL instead! */
4840         }
4841
4842         if(!config->errors)
4843           config->errors = stderr;
4844
4845         if((!outfile || !strcmp(outfile, "-")) && !config->use_ascii) {
4846           /* We get the output to stdout and we have not got the ASCII/text
4847              flag, then set stdout to be binary */
4848           SET_BINMODE(stdout);
4849         }
4850
4851         if(1 == config->tcp_nodelay)
4852           my_setopt(curl, CURLOPT_TCP_NODELAY, 1);
4853
4854         /* where to store */
4855         my_setopt(curl, CURLOPT_WRITEDATA, &outs);
4856         /* what call to write */
4857         my_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
4858
4859         /* for uploads */
4860         input.fd = infd;
4861         input.config = config;
4862         my_setopt(curl, CURLOPT_READDATA, &input);
4863         /* what call to read */
4864         my_setopt(curl, CURLOPT_READFUNCTION, my_fread);
4865
4866         /* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what
4867            CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */
4868         my_setopt(curl, CURLOPT_SEEKDATA, &input);
4869         my_setopt(curl, CURLOPT_SEEKFUNCTION, my_seek);
4870
4871         if(config->recvpersecond)
4872           /* tell libcurl to use a smaller sized buffer as it allows us to
4873              make better sleeps! 7.9.9 stuff! */
4874           my_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond);
4875
4876         /* size of uploaded file: */
4877         my_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);
4878         my_setopt_str(curl, CURLOPT_URL, url);     /* what to fetch */
4879         my_setopt_str(curl, CURLOPT_PROXY, config->proxy); /* proxy to use */
4880         if(config->proxy)
4881           my_setopt(curl, CURLOPT_PROXYTYPE, config->proxyver);
4882         my_setopt(curl, CURLOPT_NOPROGRESS, config->noprogress);
4883         if(config->no_body) {
4884           my_setopt(curl, CURLOPT_NOBODY, 1);
4885           my_setopt(curl, CURLOPT_HEADER, 1);
4886         }
4887         else
4888           my_setopt(curl, CURLOPT_HEADER, config->include_headers);
4889
4890         my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror);
4891         my_setopt(curl, CURLOPT_UPLOAD, uploadfile?TRUE:FALSE);
4892         my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly);
4893         my_setopt(curl, CURLOPT_APPEND, config->ftp_append);
4894
4895         if (config->netrc_opt)
4896           my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
4897         else if (config->netrc)
4898           my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED);
4899         else
4900           my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED);
4901
4902         my_setopt(curl, CURLOPT_FOLLOWLOCATION, config->followlocation);
4903         my_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, config->unrestricted_auth);
4904         my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii);
4905         my_setopt_str(curl, CURLOPT_USERPWD, config->userpwd);
4906         my_setopt_str(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd);
4907         my_setopt(curl, CURLOPT_NOPROXY, config->noproxy);
4908         my_setopt_str(curl, CURLOPT_RANGE, config->range);
4909         my_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
4910         my_setopt(curl, CURLOPT_TIMEOUT, config->timeout);
4911
4912         switch(config->httpreq) {
4913         case HTTPREQ_SIMPLEPOST:
4914           my_setopt_str(curl, CURLOPT_POSTFIELDS, config->postfields);
4915           my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, config->postfieldsize);
4916           break;
4917         case HTTPREQ_POST:
4918           my_setopt(curl, CURLOPT_HTTPPOST, config->httppost);
4919           break;
4920         default:
4921           break;
4922         }
4923         my_setopt_str(curl, CURLOPT_REFERER, config->referer);
4924         my_setopt(curl, CURLOPT_AUTOREFERER, config->autoreferer);
4925         my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent);
4926         my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport);
4927         my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
4928                   config->low_speed_limit);
4929         my_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time);
4930         my_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE,
4931                   config->sendpersecond);
4932         my_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE,
4933                   config->recvpersecond);
4934         my_setopt(curl, CURLOPT_RESUME_FROM_LARGE,
4935                   config->use_resume?config->resume_from:0);
4936         my_setopt_str(curl, CURLOPT_COOKIE, config->cookie);
4937         my_setopt(curl, CURLOPT_HTTPHEADER, config->headers);
4938         my_setopt(curl, CURLOPT_SSLCERT, config->cert);
4939         my_setopt_str(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
4940         my_setopt(curl, CURLOPT_SSLKEY, config->key);
4941         my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type);
4942         my_setopt_str(curl, CURLOPT_KEYPASSWD, config->key_passwd);
4943
4944         /* SSH private key uses the same command-line option as SSL private
4945            key */
4946         my_setopt_str(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key);
4947         my_setopt_str(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey);
4948
4949         /* SSH host key md5 checking allows us to fail if we are
4950          * not talking to who we think we should
4951          */
4952         my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5,
4953                       config->hostpubmd5);
4954
4955         /* default to strict verifyhost */
4956         my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
4957         if(config->cacert || config->capath) {
4958           if (config->cacert)
4959             my_setopt_str(curl, CURLOPT_CAINFO, config->cacert);
4960
4961           if (config->capath)
4962             my_setopt_str(curl, CURLOPT_CAPATH, config->capath);
4963           my_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE);
4964         }
4965         if (config->crlfile)
4966           my_setopt_str(curl, CURLOPT_CRLFILE, config->crlfile);
4967         if(config->insecure_ok) {
4968           /* new stuff needed for libcurl 7.10 */
4969           my_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
4970           my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
4971         }
4972         else {
4973           char *home = homedir();
4974           char *file = aprintf("%s/%sssh/known_hosts", home, DOT_CHAR);
4975           if(home)
4976             free(home);
4977
4978           if(file) {
4979             my_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, file);
4980             curl_free(file);
4981           }
4982           else {
4983             /* Free the list of remaining URLs and globbed upload files
4984              * to force curl to exit immediately
4985              */
4986             if(urls) {
4987               glob_cleanup(urls);
4988               urls = NULL;
4989             }
4990             if(inglob) {
4991               glob_cleanup(inglob);
4992               inglob = NULL;
4993             }
4994
4995             res = CURLE_OUT_OF_MEMORY;
4996             goto quit_urls;
4997           }
4998         }
4999
5000         if(config->no_body || config->remote_time) {
5001           /* no body or use remote time */
5002           my_setopt(curl, CURLOPT_FILETIME, TRUE);
5003         }
5004
5005         my_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs);
5006         my_setopt(curl, CURLOPT_CRLF, config->crlf);
5007         my_setopt(curl, CURLOPT_QUOTE, config->quote);
5008         my_setopt(curl, CURLOPT_POSTQUOTE, config->postquote);
5009         my_setopt(curl, CURLOPT_PREQUOTE, config->prequote);
5010         my_setopt(curl, CURLOPT_HEADERDATA,
5011                   config->headerfile?&heads:NULL);
5012         my_setopt_str(curl, CURLOPT_COOKIEFILE, config->cookiefile);
5013         /* cookie jar was added in 7.9 */
5014         if(config->cookiejar)
5015           my_setopt_str(curl, CURLOPT_COOKIEJAR, config->cookiejar);
5016         /* cookie session added in 7.9.7 */
5017         my_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession);
5018
5019         my_setopt(curl, CURLOPT_SSLVERSION, config->ssl_version);
5020         my_setopt(curl, CURLOPT_TIMECONDITION, config->timecond);
5021         my_setopt(curl, CURLOPT_TIMEVALUE, config->condtime);
5022         my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
5023         my_setopt(curl, CURLOPT_STDERR, config->errors);
5024
5025         /* three new ones in libcurl 7.3: */
5026         my_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel);
5027         my_setopt_str(curl, CURLOPT_INTERFACE, config->iface);
5028         my_setopt_str(curl, CURLOPT_KRBLEVEL, config->krblevel);
5029
5030         progressbarinit(&progressbar, config);
5031         if((config->progressmode == CURL_PROGRESS_BAR) &&
5032            !config->noprogress && !config->mute) {
5033           /* we want the alternative style, then we have to implement it
5034              ourselves! */
5035           my_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress);
5036           my_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
5037         }
5038
5039         /* new in libcurl 7.6.2: */
5040         my_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options);
5041
5042         /* new in libcurl 7.7: */
5043         my_setopt_str(curl, CURLOPT_RANDOM_FILE, config->random_file);
5044         my_setopt(curl, CURLOPT_EGDSOCKET, config->egd_file);
5045         my_setopt(curl, CURLOPT_CONNECTTIMEOUT, config->connecttimeout);
5046
5047         if(config->cipher_list)
5048           my_setopt_str(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list);
5049
5050         if(config->httpversion)
5051           my_setopt(curl, CURLOPT_HTTP_VERSION, config->httpversion);
5052
5053         /* new in libcurl 7.9.2: */
5054         if(config->disable_epsv)
5055           /* disable it */
5056           my_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE);
5057
5058         /* new in libcurl 7.10.5 */
5059         if(config->disable_eprt)
5060           /* disable it */
5061           my_setopt(curl, CURLOPT_FTP_USE_EPRT, FALSE);
5062
5063         /* new in libcurl 7.10.6 (default is Basic) */
5064         if(config->authtype)
5065           my_setopt(curl, CURLOPT_HTTPAUTH, config->authtype);
5066
5067         if(config->tracetype != TRACE_NONE) {
5068           my_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);
5069           my_setopt(curl, CURLOPT_DEBUGDATA, config);
5070           my_setopt(curl, CURLOPT_VERBOSE, TRUE);
5071         }
5072
5073         res = CURLE_OK;
5074
5075         /* new in curl ?? */
5076         if (config->engine) {
5077           res = my_setopt_str(curl, CURLOPT_SSLENGINE, config->engine);
5078           my_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1);
5079         }
5080
5081         if (res != CURLE_OK)
5082            goto show_error;
5083
5084         /* new in curl 7.10 */
5085         my_setopt_str(curl, CURLOPT_ENCODING,
5086                   (config->encoding) ? "" : NULL);
5087
5088         /* new in curl 7.10.7, extended in 7.19.4 but this only sets 0 or 1 */
5089         my_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS,
5090                   config->ftp_create_dirs);
5091         if(config->proxyanyauth)
5092           my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
5093         else if(config->proxynegotiate)
5094           my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_GSSNEGOTIATE);
5095         else if(config->proxyntlm)
5096           my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
5097         else if(config->proxydigest)
5098           my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
5099         else if(config->proxybasic)
5100           my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
5101
5102         /* new in curl 7.10.8 */
5103         if(config->max_filesize)
5104           my_setopt(curl, CURLOPT_MAXFILESIZE_LARGE,
5105                            config->max_filesize);
5106
5107         if(4 == config->ip_version)
5108           my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
5109         else if(6 == config->ip_version)
5110           my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
5111         else
5112           my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER);
5113
5114         /* new in curl 7.15.5 */
5115         if(config->ftp_ssl_reqd)
5116           my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
5117
5118         /* new in curl 7.11.0 */
5119         else if(config->ftp_ssl)
5120           my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
5121
5122         /* new in curl 7.16.0 */
5123         else if(config->ftp_ssl_control)
5124           my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_CONTROL);
5125
5126         /* new in curl 7.16.1 */
5127         if(config->ftp_ssl_ccc)
5128             my_setopt(curl, CURLOPT_FTP_SSL_CCC, config->ftp_ssl_ccc_mode);
5129
5130         /* new in curl 7.11.1, modified in 7.15.2 */
5131         if(config->socksproxy) {
5132           my_setopt_str(curl, CURLOPT_PROXY, config->socksproxy);
5133           my_setopt(curl, CURLOPT_PROXYTYPE, config->socksver);
5134         }
5135
5136 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
5137         /* new in curl 7.19.4 */
5138         if(config->socks5_gssapi_service)
5139           my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_SERVICE,
5140                     config->socks5_gssapi_service);
5141
5142         /* new in curl 7.19.4 */
5143         if(config->socks5_gssapi_nec)
5144           my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_NEC,
5145                         config->socks5_gssapi_nec);
5146 #endif
5147         /* curl 7.13.0 */
5148         my_setopt_str(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account);
5149
5150         my_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl);
5151
5152         /* curl 7.14.2 */
5153         my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip);
5154
5155         /* curl 7.15.1 */
5156         my_setopt(curl, CURLOPT_FTP_FILEMETHOD, config->ftp_filemethod);
5157
5158         /* curl 7.15.2 */
5159         if(config->localport) {
5160           my_setopt(curl, CURLOPT_LOCALPORT, config->localport);
5161           my_setopt_str(curl, CURLOPT_LOCALPORTRANGE,
5162                     config->localportrange);
5163         }
5164
5165         /* curl 7.15.5 */
5166         my_setopt_str(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER,
5167                   config->ftp_alternative_to_user);
5168
5169         /* curl 7.16.0 */
5170         my_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE,
5171                   !config->disable_sessionid);
5172
5173         /* curl 7.16.2 */
5174         if(config->raw) {
5175           my_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, FALSE);
5176           my_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, FALSE);
5177         }
5178
5179         /* curl 7.17.1 */
5180         if (!config->nokeepalive) {
5181           my_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockoptcallback);
5182           my_setopt(curl, CURLOPT_SOCKOPTDATA, config);
5183         }
5184
5185         /* curl 7.19.1 (the 301 version existed in 7.18.2) */
5186         my_setopt(curl, CURLOPT_POSTREDIR, config->post301 |
5187                   (config->post302 ? CURL_REDIR_POST_302 : FALSE));
5188
5189         /* curl 7.20.0 */
5190         if(config->tftp_blksize)
5191           my_setopt(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize);
5192
5193         if(config->mail_from)
5194           my_setopt_str(curl, CURLOPT_MAIL_FROM, config->mail_from);
5195
5196         if(config->mail_rcpt)
5197           my_setopt(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt);
5198
5199         /* curl 7.20.x */
5200         if(config->ftp_pret)
5201           my_setopt(curl, CURLOPT_FTP_USE_PRET, TRUE);
5202
5203         if ((urlnode->flags & GETOUT_USEREMOTE)
5204             && config->content_disposition) {
5205           my_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
5206           my_setopt(curl, CURLOPT_HEADERDATA, &outs);
5207         }
5208
5209         retry_numretries = config->req_retry;
5210
5211         retrystart = cutil_tvnow();
5212
5213         for(;;) {
5214           res = curl_easy_perform(curl);
5215           if (!curl_slist_append(easycode, "ret = curl_easy_perform(hnd);")) {
5216             res = CURLE_OUT_OF_MEMORY;
5217             break;
5218           }
5219
5220           if (config->content_disposition && outs.stream && !config->mute)
5221             printf("curl: Saved to filename '%s'\n", outs.filename);
5222
5223           /* if retry-max-time is non-zero, make sure we haven't exceeded the
5224              time */
5225           if(retry_numretries &&
5226              (!config->retry_maxtime ||
5227               (cutil_tvdiff(cutil_tvnow(), retrystart)<
5228                config->retry_maxtime*1000)) ) {
5229             enum {
5230               RETRY_NO,
5231               RETRY_TIMEOUT,
5232               RETRY_HTTP,
5233               RETRY_FTP,
5234               RETRY_LAST /* not used */
5235             } retry = RETRY_NO;
5236             long response;
5237             if(CURLE_OPERATION_TIMEDOUT == res)
5238               /* retry timeout always */
5239               retry = RETRY_TIMEOUT;
5240             else if(CURLE_OK == res) {
5241               /* Check for HTTP transient errors */
5242               char *this_url=NULL;
5243               curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &this_url);
5244               if(this_url &&
5245                  curlx_strnequal(this_url, "http", 4)) {
5246                 /* This was HTTP(S) */
5247                 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
5248
5249                 switch(response) {
5250                 case 500: /* Internal Server Error */
5251                 case 502: /* Bad Gateway */
5252                 case 503: /* Service Unavailable */
5253                 case 504: /* Gateway Timeout */
5254                   retry = RETRY_HTTP;
5255                   /*
5256                    * At this point, we have already written data to the output
5257                    * file (or terminal). If we write to a file, we must rewind
5258                    * or close/re-open the file so that the next attempt starts
5259                    * over from the beginning.
5260                    *
5261                    * TODO: similar action for the upload case. We might need
5262                    * to start over reading from a previous point if we have
5263                    * uploaded something when this was returned.
5264                    */
5265                   break;
5266                 }
5267               }
5268             } /* if CURLE_OK */
5269             else if(res) {
5270               curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
5271
5272               if(response/100 == 4)
5273                 /*
5274                  * This is typically when the FTP server only allows a certain
5275                  * amount of users and we are not one of them.  All 4xx codes
5276                  * are transient.
5277                  */
5278                 retry = RETRY_FTP;
5279             }
5280
5281             if(retry) {
5282               static const char * const m[]={
5283                 NULL, "timeout", "HTTP error", "FTP error"
5284               };
5285               warnf(config, "Transient problem: %s "
5286                     "Will retry in %ld seconds. "
5287                     "%ld retries left.\n",
5288                     m[retry], retry_sleep/1000, retry_numretries);
5289
5290               go_sleep(retry_sleep);
5291               retry_numretries--;
5292               if(!config->retry_delay) {
5293                 retry_sleep *= 2;
5294                 if(retry_sleep > RETRY_SLEEP_MAX)
5295                   retry_sleep = RETRY_SLEEP_MAX;
5296               }
5297               if(outs.bytes && outs.filename) {
5298                 /* We have written data to a output file, we truncate file
5299                  */
5300                 if(!config->mute)
5301                   fprintf(config->errors, "Throwing away %"
5302                           CURL_FORMAT_CURL_OFF_T " bytes\n",
5303                           outs.bytes);
5304                 fflush(outs.stream);
5305                 /* truncate file at the position where we started appending */
5306 #ifdef HAVE_FTRUNCATE
5307                 if(ftruncate( fileno(outs.stream), outs.init)) {
5308                   /* when truncate fails, we can't just append as then we'll
5309                      create something strange, bail out */
5310                   if(!config->mute)
5311                     fprintf(config->errors,
5312                             "failed to truncate, exiting\n");
5313                   break;
5314                 }
5315                 /* now seek to the end of the file, the position where we
5316                    just truncated the file in a large file-safe way */
5317                 fseek(outs.stream, 0, SEEK_END);
5318 #else
5319                 /* ftruncate is not available, so just reposition the file
5320                    to the location we would have truncated it. This won't
5321                    work properly with large files on 32-bit systems, but
5322                    most of those will have ftruncate. */
5323                 fseek(outs.stream, (long)outs.init, SEEK_SET);
5324 #endif
5325                 outs.bytes = 0; /* clear for next round */
5326               }
5327               continue;
5328             }
5329           } /* if retry_numretries */
5330
5331           /* In all ordinary cases, just break out of loop here */
5332           retry_sleep = retry_sleep_default;
5333           break;
5334
5335         }
5336
5337         if((config->progressmode == CURL_PROGRESS_BAR) &&
5338            progressbar.calls)
5339           /* if the custom progress bar has been displayed, we output a
5340              newline here */
5341           fputs("\n", progressbar.out);
5342
5343         if(config->writeout)
5344           ourWriteOut(curl, config->writeout);
5345 #ifdef USE_ENVIRONMENT
5346         if (config->writeenv)
5347           ourWriteEnv(curl);
5348 #endif
5349
5350 show_error:
5351
5352 #ifdef __VMS
5353         if(is_vms_shell()) {
5354           /* VMS DCL shell behavior */
5355           if(!config->showerror) {
5356             vms_show = VMSSTS_HIDE;
5357           }
5358         }
5359         else
5360 #endif
5361         {
5362           if((res!=CURLE_OK) && config->showerror) {
5363             fprintf(config->errors, "curl: (%d) %s\n", res,
5364                     errorbuffer[0]? errorbuffer:
5365                     curl_easy_strerror((CURLcode)res));
5366             if(CURLE_SSL_CACERT == res) {
5367 #define CURL_CA_CERT_ERRORMSG1 \
5368 "More details here: http://curl.haxx.se/docs/sslcerts.html\n\n" \
5369 "curl performs SSL certificate verification by default, using a \"bundle\"\n" \
5370 " of Certificate Authority (CA) public keys (CA certs). If the default\n" \
5371 " bundle file isn't adequate, you can specify an alternate file\n" \
5372 " using the --cacert option.\n"
5373
5374 #define CURL_CA_CERT_ERRORMSG2 \
5375 "If this HTTPS server uses a certificate signed by a CA represented in\n" \
5376 " the bundle, the certificate verification probably failed due to a\n" \
5377 " problem with the certificate (it might be expired, or the name might\n" \
5378 " not match the domain name in the URL).\n" \
5379 "If you'd like to turn off curl's verification of the certificate, use\n" \
5380 " the -k (or --insecure) option.\n"
5381
5382               fprintf(config->errors, "%s%s",
5383                       CURL_CA_CERT_ERRORMSG1,
5384                       CURL_CA_CERT_ERRORMSG2 );
5385             }
5386           }
5387         }
5388
5389         if (outfile && !curlx_strequal(outfile, "-") && outs.stream) {
5390           int rc = fclose(outs.stream);
5391           if(!res && rc) {
5392             /* something went wrong in the writing process */
5393             res = CURLE_WRITE_ERROR;
5394             fprintf(config->errors, "(%d) Failed writing body\n", res);
5395           }
5396         }
5397
5398 #ifdef HAVE_UTIME
5399         /* Important that we set the time _after_ the file has been
5400            closed, as is done above here */
5401         if(config->remote_time && outs.filename) {
5402           /* ask libcurl if we got a time. Pretty please */
5403           long filetime;
5404           curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
5405           if(filetime >= 0) {
5406             struct utimbuf times;
5407             times.actime = (time_t)filetime;
5408             times.modtime = (time_t)filetime;
5409             utime(outs.filename, &times); /* set the time we got */
5410           }
5411         }
5412 #endif
5413 #ifdef __AMIGA__
5414         /* Set the url as comment for the file. (up to 80 chars are allowed)
5415          */
5416         if( strlen(url) > 78 )
5417           url[79] = '\0';
5418
5419         SetComment( outs.filename, url);
5420 #endif
5421
5422 quit_urls:
5423         if(url)
5424           free(url);
5425
5426         if(outfile)
5427           free(outfile);
5428
5429         if(infdopen)
5430           close(infd);
5431
5432       } /* loop to the next URL */
5433
5434       if(urls) {
5435         /* cleanup memory used for URL globbing patterns */
5436         glob_cleanup(urls);
5437         urls = NULL;
5438       }
5439
5440       if(uploadfile)
5441         free(uploadfile);
5442
5443     } /* loop to the next globbed upload file */
5444
5445     if(inglob) {
5446       glob_cleanup(inglob);
5447       inglob = NULL;
5448     }
5449
5450     if(outfiles)
5451       free(outfiles);
5452
5453     /* empty this urlnode struct */
5454     if(urlnode->url)
5455       free(urlnode->url);
5456     if(urlnode->outfile)
5457       free(urlnode->outfile);
5458     if(urlnode->infile)
5459       free(urlnode->infile);
5460
5461     /* move on to the next URL */
5462     nextnode=urlnode->next;
5463     free(urlnode); /* free the node */
5464     urlnode = nextnode;
5465
5466   } /* while-loop through all URLs */
5467
5468 quit_curl:
5469   if (httpgetfields)
5470     free(httpgetfields);
5471
5472   if (config->engine)
5473     free(config->engine);
5474
5475   /* cleanup the curl handle! */
5476   curl_easy_cleanup(curl);
5477   config->easy = NULL; /* cleanup now */
5478   if (easycode)
5479     curl_slist_append(easycode, "curl_easy_cleanup(hnd);");
5480
5481   if(heads.stream && (heads.stream != stdout))
5482     fclose(heads.stream);
5483
5484   if(allocuseragent)
5485     free(config->useragent);
5486
5487   if(config->trace_fopened && config->trace_stream)
5488     fclose(config->trace_stream);
5489
5490   /* Dump the libcurl code if previously enabled.
5491      NOTE: that this function relies on config->errors amongst other things
5492      so not everything can be closed and cleaned before this is called */
5493   dumpeasycode(config);
5494
5495   if(config->errors_fopened)
5496     fclose(config->errors);
5497
5498   main_free(); /* cleanup */
5499
5500   return res;
5501 }
5502
5503 /* Ensure that file descriptors 0, 1 and 2 (stdin, stdout, stderr) are
5504    open before starting to run.  Otherwise, the first three network
5505    sockets opened by curl could be used for input sources, downloaded data
5506    or error logs as they will effectively be stdin, stdout and/or stderr.
5507 */
5508 static void checkfds(void)
5509 {
5510 #ifdef HAVE_PIPE
5511   int fd[2] = { STDIN_FILENO, STDIN_FILENO };
5512   while( fd[0] == STDIN_FILENO ||
5513          fd[0] == STDOUT_FILENO ||
5514          fd[0] == STDERR_FILENO ||
5515          fd[1] == STDIN_FILENO ||
5516          fd[1] == STDOUT_FILENO ||
5517          fd[1] == STDERR_FILENO )
5518     if (pipe(fd) < 0)
5519       return;   /* Out of handles. This isn't really a big problem now, but
5520                    will be when we try to create a socket later. */
5521   close(fd[0]);
5522   close(fd[1]);
5523 #endif
5524 }
5525
5526
5527
5528 int main(int argc, char *argv[])
5529 {
5530   int res;
5531   struct Configurable config;
5532
5533   memset(&config, 0, sizeof(struct Configurable));
5534
5535   config.errors = stderr; /* default errors to stderr */
5536
5537   checkfds();
5538
5539   res = operate(&config, argc, argv);
5540 #ifdef __SYMBIAN32__
5541   if (config.showerror)
5542     pressanykey();
5543 #endif
5544   free_config_fields(&config);
5545
5546 #ifdef __NOVELL_LIBC__
5547   if (getenv("_IN_NETWARE_BASH_") == NULL)
5548     pressanykey();
5549 #endif
5550 #ifdef __VMS
5551   vms_special_exit(res, vms_show);
5552 #else
5553   return res;
5554 #endif
5555 }
5556
5557 /*
5558  * Reads a line from the given file, ensuring is NUL terminated.
5559  * The pointer must be freed by the caller.
5560  * NULL is returned on an out of memory condition.
5561  */
5562 static char *my_get_line(FILE *fp)
5563 {
5564    char buf[4096];
5565    char *nl = NULL;
5566    char *retval = NULL;
5567
5568    do {
5569      if (NULL == fgets(buf, sizeof(buf), fp))
5570        break;
5571      if (NULL == retval) {
5572        retval = strdup(buf);
5573        if(!retval)
5574          return NULL;
5575      }
5576      else {
5577        char *ptr;
5578        ptr = realloc(retval, strlen(retval) + strlen(buf) + 1);
5579        if (NULL == ptr) {
5580          free(retval);
5581          return NULL;
5582        }
5583        retval = ptr;
5584        strcat(retval, buf);
5585      }
5586    }
5587    while (NULL == (nl = strchr(retval, '\n')));
5588
5589    if (NULL != nl)
5590      *nl = '\0';
5591
5592    return retval;
5593 }
5594
5595 static void show_dir_errno(FILE *errors, const char *name)
5596 {
5597   switch (ERRNO) {
5598 #ifdef EACCES
5599   case EACCES:
5600     fprintf(errors,"You don't have permission to create %s.\n", name);
5601     break;
5602 #endif
5603 #ifdef ENAMETOOLONG
5604   case ENAMETOOLONG:
5605     fprintf(errors,"The directory name %s is too long.\n", name);
5606     break;
5607 #endif
5608 #ifdef EROFS
5609   case EROFS:
5610     fprintf(errors,"%s resides on a read-only file system.\n", name);
5611     break;
5612 #endif
5613 #ifdef ENOSPC
5614   case ENOSPC:
5615     fprintf(errors,"No space left on the file system that will "
5616             "contain the directory %s.\n", name);
5617     break;
5618 #endif
5619 #ifdef EDQUOT
5620   case EDQUOT:
5621     fprintf(errors,"Cannot create directory %s because you "
5622             "exceeded your quota.\n", name);
5623     break;
5624 #endif
5625   default :
5626     fprintf(errors,"Error creating directory %s.\n", name);
5627     break;
5628   }
5629 }
5630
5631 /* Create the needed directory hierarchy recursively in order to save
5632    multi-GETs in file output, ie:
5633    curl "http://my.site/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt"
5634    should create all the dir* automagically
5635 */
5636 static int create_dir_hierarchy(const char *outfile, FILE *errors)
5637 {
5638   char *tempdir;
5639   char *tempdir2;
5640   char *outdup;
5641   char *dirbuildup;
5642   int result=0;
5643
5644   outdup = strdup(outfile);
5645   if(!outdup)
5646     return -1;
5647
5648   dirbuildup = malloc(sizeof(char) * strlen(outfile));
5649   if(!dirbuildup) {
5650     free(outdup);
5651     return -1;
5652   }
5653   dirbuildup[0] = '\0';
5654
5655   tempdir = strtok(outdup, DIR_CHAR);
5656
5657   while (tempdir != NULL) {
5658     tempdir2 = strtok(NULL, DIR_CHAR);
5659     /* since strtok returns a token for the last word even
5660        if not ending with DIR_CHAR, we need to prune it */
5661     if (tempdir2 != NULL) {
5662       size_t dlen = strlen(dirbuildup);
5663       if (dlen)
5664         sprintf(&dirbuildup[dlen], "%s%s", DIR_CHAR, tempdir);
5665       else {
5666         if (0 != strncmp(outdup, DIR_CHAR, 1))
5667           strcpy(dirbuildup, tempdir);
5668         else
5669           sprintf(dirbuildup, "%s%s", DIR_CHAR, tempdir);
5670       }
5671       if (access(dirbuildup, F_OK) == -1) {
5672         result = mkdir(dirbuildup,(mode_t)0000750);
5673         if (-1 == result) {
5674           show_dir_errno(errors, dirbuildup);
5675           break; /* get out of loop */
5676         }
5677       }
5678     }
5679     tempdir = tempdir2;
5680   }
5681   free(dirbuildup);
5682   free(outdup);
5683
5684   return result; /* 0 is fine, -1 is badness */
5685 }
5686
5687 #if defined(MSDOS) || defined(WIN32)
5688
5689 #ifndef HAVE_BASENAME
5690 /* basename() returns a pointer to the last component of a pathname.
5691  * Ripped from lib/formdata.c.
5692  */
5693 static char *Curl_basename(char *path)
5694 {
5695   /* Ignore all the details above for now and make a quick and simple
5696      implementaion here */
5697   char *s1;
5698   char *s2;
5699
5700   s1=strrchr(path, '/');
5701   s2=strrchr(path, '\\');
5702
5703   if(s1 && s2) {
5704     path = (s1 > s2? s1 : s2)+1;
5705   }
5706   else if(s1)
5707     path = s1 + 1;
5708   else if(s2)
5709     path = s2 + 1;
5710
5711   return path;
5712 }
5713 #define basename(x) Curl_basename((x))
5714 #endif /* HAVE_BASENAME */
5715
5716 /* The following functions are taken with modification from the DJGPP
5717  * port of tar 1.12. They use algorithms originally from DJTAR. */
5718
5719 static const char *
5720 msdosify (const char *file_name)
5721 {
5722   static char dos_name[PATH_MAX];
5723   static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */
5724                                        "|<>\\\":?*"; /* illegal in DOS & W95 */
5725   static const char *illegal_chars_w95 = &illegal_chars_dos[8];
5726   int idx, dot_idx;
5727   const char *s = file_name;
5728   char *d = dos_name;
5729   const char * const dlimit = dos_name + sizeof(dos_name) - 1;
5730   const char *illegal_aliens = illegal_chars_dos;
5731   size_t len = sizeof (illegal_chars_dos) - 1;
5732
5733   /* Support for Windows 9X VFAT systems, when available. */
5734   if (_use_lfn (file_name)) {
5735     illegal_aliens = illegal_chars_w95;
5736     len -= (illegal_chars_w95 - illegal_chars_dos);
5737   }
5738
5739   /* Get past the drive letter, if any. */
5740   if (s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
5741     *d++ = *s++;
5742     *d++ = *s++;
5743   }
5744
5745   for (idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) {
5746     if (memchr (illegal_aliens, *s, len)) {
5747       /* Dots are special: DOS doesn't allow them as the leading character,
5748          and a file name cannot have more than a single dot.  We leave the
5749          first non-leading dot alone, unless it comes too close to the
5750          beginning of the name: we want sh.lex.c to become sh_lex.c, not
5751          sh.lex-c.  */
5752       if (*s == '.') {
5753         if (idx == 0 && (s[1] == '/' || (s[1] == '.' && s[2] == '/'))) {
5754           /* Copy "./" and "../" verbatim.  */
5755           *d++ = *s++;
5756           if (*s == '.')
5757             *d++ = *s++;
5758           *d = *s;
5759         }
5760         else if (idx == 0)
5761           *d = '_';
5762         else if (dot_idx >= 0) {
5763           if (dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */
5764             d[dot_idx - idx] = '_'; /* replace previous dot */
5765             *d = '.';
5766           }
5767           else
5768             *d = '-';
5769         }
5770         else
5771           *d = '.';
5772
5773         if (*s == '.')
5774           dot_idx = idx;
5775       }
5776       else if (*s == '+' && s[1] == '+') {
5777         if (idx - 2 == dot_idx) { /* .c++, .h++ etc. */
5778           *d++ = 'x';
5779           *d   = 'x';
5780         }
5781         else {
5782           /* libg++ etc.  */
5783           memcpy (d, "plus", 4);
5784           d += 3;
5785         }
5786         s++;
5787         idx++;
5788       }
5789       else
5790         *d = '_';
5791     }
5792     else
5793       *d = *s;
5794     if (*s == '/') {
5795       idx = 0;
5796       dot_idx = -1;
5797     }
5798     else
5799       idx++;
5800   }
5801
5802   *d = '\0';
5803   return dos_name;
5804 }
5805
5806 static char *
5807 rename_if_dos_device_name (char *file_name)
5808 {
5809   /* We could have a file whose name is a device on MS-DOS.  Trying to
5810    * retrieve such a file would fail at best and wedge us at worst.  We need
5811    * to rename such files. */
5812   char *base;
5813   struct_stat st_buf;
5814   char fname[PATH_MAX];
5815
5816   strncpy(fname, file_name, PATH_MAX-1);
5817   fname[PATH_MAX-1] = 0;
5818   base = basename(fname);
5819   if (((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
5820     size_t blen = strlen (base);
5821
5822     if (strlen(fname) >= PATH_MAX-1) {
5823       /* Make room for the '_' */
5824       blen--;
5825       base[blen] = 0;
5826     }
5827     /* Prepend a '_'.  */
5828     memmove (base + 1, base, blen + 1);
5829     base[0] = '_';
5830     strcpy (file_name, fname);
5831   }
5832   return file_name;
5833 }
5834
5835 /* Replace bad characters in the file name before using it.
5836  * fn will always be freed before return
5837  * The returned pointer must be freed by the caller if not NULL
5838  */
5839 static char *sanitize_dos_name(char *fn)
5840 {
5841   char tmpfn[PATH_MAX];
5842   if(strlen(fn) >= PATH_MAX)
5843     fn[PATH_MAX-1]=0; /* truncate it */
5844   strcpy(tmpfn, msdosify(fn));
5845   free(fn);
5846   return strdup(rename_if_dos_device_name(tmpfn));
5847 }
5848 #endif /* MSDOS || WIN32 */