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