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