Initial commit of visual studio 9 git build superproject.
[git-build-vc9.git] / curl / src / main.c
blob7ef86069f970ce723ed94f62dce61fa834019e93
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: main.c,v 1.479 2008-08-28 22:41:35 yangtse Exp $
22 ***************************************************************************/
23 #include "setup.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdarg.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <ctype.h>
32 #include <errno.h>
34 #include <curl/curl.h>
36 #include "urlglob.h"
37 #include "writeout.h"
38 #include "getpass.h"
39 #include "homedir.h"
40 #include "curlutil.h"
41 #ifdef USE_MANUAL
42 #include "hugehelp.h"
43 #endif
44 #ifdef USE_ENVIRONMENT
45 #include "writeenv.h"
46 #endif
48 #define CURLseparator "--_curl_--"
50 #ifdef NETWARE
51 #ifdef __NOVELL_LIBC__
52 #include <screen.h>
53 #else
54 #include <nwconio.h>
55 #define mkdir mkdir_510
56 #endif
57 #endif
59 #include "version.h"
61 #ifdef HAVE_IO_H /* typical win32 habit */
62 #include <io.h>
63 #endif
65 #ifdef HAVE_UNISTD_H
66 #include <unistd.h>
67 #endif
69 #ifdef HAVE_FCNTL_H
70 #include <fcntl.h>
71 #endif
73 #ifdef HAVE_UTIME_H
74 #include <utime.h>
75 #else
76 #ifdef HAVE_SYS_UTIME_H
77 #include <sys/utime.h>
78 #endif
80 #endif /* HAVE_UTIME_H */
82 #ifdef HAVE_LIMITS_H
83 #include <limits.h>
84 #endif
86 #ifdef HAVE_SYS_POLL_H
87 #include <sys/poll.h>
88 #elif defined(HAVE_POLL_H)
89 #include <poll.h>
90 #endif
92 #ifdef HAVE_LOCALE_H
93 #include <locale.h> /* for setlocale() */
94 #endif
96 #define ENABLE_CURLX_PRINTF
97 /* make the curlx header define all printf() functions to use the curlx_*
98 versions instead */
99 #include <curlx.h> /* header from the libcurl directory */
101 #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
102 #include <iconv.h>
103 /* set default codesets for iconv */
104 #ifndef CURL_ICONV_CODESET_OF_NETWORK
105 #define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1"
106 #endif
107 #endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
109 #ifdef HAVE_NETINET_IN_H
110 #include <netinet/in.h> /* for IPPROTO_TCP */
111 #endif
112 #ifdef HAVE_NETINET_TCP_H
113 #include <netinet/tcp.h> /* for TCP_KEEPIDLE, TCP_KEEPINTVL */
114 #endif
116 /* The last #include file should be: */
117 #ifdef CURLDEBUG
118 #ifndef CURLTOOLDEBUG
119 #define MEMDEBUG_NODEFINES
120 #endif
121 /* This is low-level hard-hacking memory leak tracking and similar. Using
122 the library level code from this client-side is ugly, but we do this
123 anyway for convenience. */
124 #include "memdebug.h"
125 #endif
127 #if defined(NETWARE)
128 #define PRINT_LINES_PAUSE 23
129 #endif
131 #if defined(__SYMBIAN32__)
132 #define PRINT_LINES_PAUSE 16
133 #define pressanykey() getchar()
134 #endif
136 #define DEFAULT_MAXREDIRS 50L
138 #if defined(O_BINARY) && defined(HAVE_SETMODE)
139 #ifdef __HIGHC__
140 #define SET_BINMODE(file) _setmode(file,O_BINARY)
141 #else
142 #define SET_BINMODE(file) setmode(fileno(file),O_BINARY)
143 #endif
144 #else
145 #define SET_BINMODE(file) ((void)0)
146 #endif
148 #ifndef O_BINARY
149 /* since O_BINARY as used in bitmasks, setting it to zero makes it usable in
150 source code but yet it doesn't ruin anything */
151 #define O_BINARY 0
152 #endif
154 #ifdef MSDOS
155 #include <dos.h>
157 static const char *msdosify(const char *);
158 static char *rename_if_dos_device_name(char *);
160 #ifdef DJGPP
161 /* we want to glob our own argv[] */
162 char **__crt0_glob_function (char *arg)
164 (void)arg;
165 return (char**)0;
167 #endif /* __DJGPP__ */
168 #endif /* MSDOS */
170 #ifndef STDIN_FILENO
171 #define STDIN_FILENO fileno(stdin)
172 #endif
174 #ifndef STDOUT_FILENO
175 #define STDOUT_FILENO fileno(stdout)
176 #endif
178 #ifndef STDERR_FILENO
179 #define STDERR_FILENO fileno(stderr)
180 #endif
182 #define CURL_PROGRESS_STATS 0 /* default progress display */
183 #define CURL_PROGRESS_BAR 1
186 * @def MIN
187 * standard MIN macro
189 #ifndef MIN
190 #define MIN(X,Y) (((X) < (Y)) ? (X) : (Y))
191 #endif
193 typedef enum {
194 HTTPREQ_UNSPEC,
195 HTTPREQ_GET,
196 HTTPREQ_HEAD,
197 HTTPREQ_POST,
198 HTTPREQ_SIMPLEPOST,
199 HTTPREQ_CUSTOM,
200 HTTPREQ_LAST
201 } HttpReq;
203 #ifdef WIN32
204 #include <direct.h>
205 #define F_OK 0
206 #define mkdir(x,y) (mkdir)(x)
207 #endif
209 #ifdef VMS
210 #include "curlmsg_vms.h"
211 #endif
214 * Large file support (>2Gb) using WIN32 functions.
217 #ifdef USE_WIN32_LARGE_FILES
218 # include <io.h>
219 # include <sys/types.h>
220 # include <sys/stat.h>
221 # define lseek(fdes,offset,whence) _lseeki64(fdes, offset, whence)
222 # define fstat(fdes,stp) _fstati64(fdes, stp)
223 # define stat(fname,stp) _stati64(fname, stp)
224 # define struct_stat struct _stati64
225 #endif
228 * Small file support (<2Gb) using WIN32 functions.
231 #ifdef USE_WIN32_SMALL_FILES
232 # include <io.h>
233 # include <sys/types.h>
234 # include <sys/stat.h>
235 # define lseek(fdes,offset,whence) _lseek(fdes, (long)offset, whence)
236 # define fstat(fdes,stp) _fstat(fdes, stp)
237 # define stat(fname,stp) _stat(fname, stp)
238 # define struct_stat struct _stat
239 #endif
241 #ifndef struct_stat
242 # define struct_stat struct stat
243 #endif
246 * Default sizeof(off_t) in case it hasn't been defined in config file.
249 #ifndef SIZEOF_OFF_T
250 # if defined(__VMS) && (defined(__alpha) || defined(__ia64))
251 # if defined(_LARGEFILE)
252 # define SIZEOF_OFF_T 8
253 # endif
254 # elif defined(__OS400__) && defined(__ILEC400__)
255 # if defined(_LARGE_FILES)
256 # define SIZEOF_OFF_T 8
257 # endif
258 # elif defined(__MVS__) && defined(__IBMC__)
259 # if defined(_LP64) || defined(_LARGE_FILES)
260 # define SIZEOF_OFF_T 8
261 # endif
262 # elif defined(__370__) && defined(__IBMC__)
263 # if defined(_LP64) || defined(_LARGE_FILES)
264 # define SIZEOF_OFF_T 8
265 # endif
266 # endif
267 # ifndef SIZEOF_OFF_T
268 # define SIZEOF_OFF_T 4
269 # endif
270 #endif
272 #ifdef CURL_DOES_CONVERSIONS
273 #ifdef HAVE_ICONV
274 iconv_t inbound_cd = (iconv_t)-1;
275 iconv_t outbound_cd = (iconv_t)-1;
278 * convert_to_network() is an internal function to convert
279 * from the host encoding to ASCII on non-ASCII platforms.
281 static CURLcode
282 convert_to_network(char *buffer, size_t length)
284 CURLcode rc;
286 /* translate from the host encoding to the network encoding */
287 char *input_ptr, *output_ptr;
288 size_t in_bytes, out_bytes;
290 /* open an iconv conversion descriptor if necessary */
291 if(outbound_cd == (iconv_t)-1) {
292 outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
293 CURL_ICONV_CODESET_OF_HOST);
294 if(outbound_cd == (iconv_t)-1) {
295 return CURLE_CONV_FAILED;
298 /* call iconv */
299 input_ptr = output_ptr = buffer;
300 in_bytes = out_bytes = length;
301 rc = iconv(outbound_cd, &input_ptr, &in_bytes,
302 &output_ptr, &out_bytes);
303 if ((rc == -1) || (in_bytes != 0)) {
304 return CURLE_CONV_FAILED;
307 return CURLE_OK;
311 * convert_from_network() is an internal function
312 * for performing ASCII conversions on non-ASCII platforms.
314 static CURLcode
315 convert_from_network(char *buffer, size_t length)
317 CURLcode rc;
319 /* translate from the network encoding to the host encoding */
320 char *input_ptr, *output_ptr;
321 size_t in_bytes, out_bytes;
323 /* open an iconv conversion descriptor if necessary */
324 if(inbound_cd == (iconv_t)-1) {
325 inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
326 CURL_ICONV_CODESET_OF_NETWORK);
327 if(inbound_cd == (iconv_t)-1) {
328 return CURLE_CONV_FAILED;
331 /* call iconv */
332 input_ptr = output_ptr = buffer;
333 in_bytes = out_bytes = length;
334 rc = iconv(inbound_cd, &input_ptr, &in_bytes,
335 &output_ptr, &out_bytes);
336 if ((rc == -1) || (in_bytes != 0)) {
337 return CURLE_CONV_FAILED;
340 return CURLE_OK;
342 #endif /* HAVE_ICONV */
344 static
345 char convert_char(curl_infotype infotype, char this_char)
347 /* determine how this specific character should be displayed */
348 switch(infotype) {
349 case CURLINFO_DATA_IN:
350 case CURLINFO_DATA_OUT:
351 case CURLINFO_SSL_DATA_IN:
352 case CURLINFO_SSL_DATA_OUT:
353 /* data, treat as ASCII */
354 if ((this_char >= 0x20) && (this_char < 0x7f)) {
355 /* printable ASCII hex value: convert to host encoding */
356 convert_from_network(&this_char, 1);
358 else {
359 /* non-printable ASCII, use a replacement character */
360 return UNPRINTABLE_CHAR;
362 /* fall through to default */
363 default:
364 /* treat as host encoding */
365 if (ISPRINT(this_char)
366 && (this_char != '\t')
367 && (this_char != '\r')
368 && (this_char != '\n')) {
369 /* printable characters excluding tabs and line end characters */
370 return this_char;
372 break;
374 /* non-printable, use a replacement character */
375 return UNPRINTABLE_CHAR;
377 #endif /* CURL_DOES_CONVERSIONS */
379 #ifdef WIN32
381 * Truncate a file handle at a 64-bit position 'where'.
382 * Borland doesn't even support 64-bit types.
384 #ifdef __BORLANDC__
385 #define _lseeki64(hnd,ofs,whence) lseek(hnd,ofs,whence)
386 #endif
388 #ifndef HAVE_FTRUNCATE
389 #define HAVE_FTRUNCATE 1
390 #endif
392 static int ftruncate64 (int fd, curl_off_t where)
394 if(_lseeki64(fd, where, SEEK_SET) < 0)
395 return -1;
397 if(!SetEndOfFile((HANDLE)_get_osfhandle(fd)))
398 return -1;
400 return 0;
402 #define ftruncate(fd,where) ftruncate64(fd,where)
403 #endif
405 typedef enum {
406 TRACE_NONE, /* no trace/verbose output at all! */
407 TRACE_BIN, /* tcpdump inspired look */
408 TRACE_ASCII, /* like *BIN but without the hex output */
409 TRACE_PLAIN /* -v/--verbose type */
410 } trace;
412 struct OutStruct {
413 char *filename;
414 FILE *stream;
415 struct Configurable *config;
416 curl_off_t bytes; /* amount written so far */
417 curl_off_t init; /* original size (non-zero when appending) */
420 struct Configurable {
421 CURL *easy; /* once we have one, we keep it here */
422 bool remote_time;
423 char *random_file;
424 char *egd_file;
425 char *useragent;
426 char *cookie; /* single line with specified cookies */
427 char *cookiejar; /* write to this file */
428 char *cookiefile; /* read from this file */
429 bool cookiesession; /* new session? */
430 bool encoding; /* Accept-Encoding please */
431 long authtype; /* auth bitmask */
432 bool use_resume;
433 bool resume_from_current;
434 bool disable_epsv;
435 bool disable_eprt;
436 curl_off_t resume_from;
437 char *postfields;
438 curl_off_t postfieldsize;
439 char *referer;
440 long timeout;
441 long connecttimeout;
442 long maxredirs;
443 curl_off_t max_filesize;
444 char *headerfile;
445 char *ftpport;
446 char *iface;
447 int localport;
448 int localportrange;
449 unsigned short porttouse;
450 char *range;
451 long low_speed_limit;
452 long low_speed_time;
453 bool showerror;
454 char *userpwd;
455 char *proxyuserpwd;
456 char *proxy;
457 bool proxytunnel;
458 bool ftp_append; /* APPE on ftp */
459 bool mute; /* shutup */
460 bool use_ascii; /* select ascii or text transfer */
461 bool autoreferer; /* automatically set referer */
462 bool failonerror; /* fail on (HTTP) errors */
463 bool include_headers; /* send headers to data output */
464 bool no_body; /* don't get the body */
465 bool dirlistonly; /* only get the FTP dir list */
466 bool followlocation; /* follow http redirects */
467 bool unrestricted_auth; /* Continue to send authentication (user+password)
468 when following ocations, even when hostname
469 changed */
470 bool netrc_opt;
471 bool netrc;
472 bool noprogress;
473 bool isatty; /* updated internally only if the output is a tty */
474 struct getout *url_list; /* point to the first node */
475 struct getout *url_last; /* point to the last/current node */
476 struct getout *url_get; /* point to the node to fill in URL */
477 struct getout *url_out; /* point to the node to fill in outfile */
478 char *cipher_list;
479 char *cert;
480 char *cert_type;
481 char *cacert;
482 char *capath;
483 char *key;
484 char *key_type;
485 char *key_passwd;
486 char *pubkey;
487 char *hostpubmd5;
488 char *engine;
489 bool list_engines;
490 bool crlf;
491 char *customrequest;
492 char *krblevel;
493 char *trace_dump; /* file to dump the network trace to, or NULL */
494 FILE *trace_stream;
495 bool trace_fopened;
496 trace tracetype;
497 bool tracetime; /* include timestamp? */
498 long httpversion;
499 bool progressmode;
500 bool nobuffer;
501 bool globoff;
502 bool use_httpget;
503 bool insecure_ok; /* set TRUE to allow insecure SSL connects */
504 bool create_dirs;
505 bool ftp_create_dirs;
506 bool ftp_skip_ip;
507 bool proxynegotiate;
508 bool proxyntlm;
509 bool proxydigest;
510 bool proxybasic;
511 bool proxyanyauth;
512 char *writeout; /* %-styled format string to output */
513 bool writeenv; /* write results to environment, if available */
514 FILE *errors; /* if stderr redirect is requested */
515 bool errors_fopened;
516 struct curl_slist *quote;
517 struct curl_slist *postquote;
518 struct curl_slist *prequote;
519 long ssl_version;
520 long ip_version;
521 curl_TimeCond timecond;
522 time_t condtime;
523 struct curl_slist *headers;
524 struct curl_httppost *httppost;
525 struct curl_httppost *last_post;
526 struct curl_slist *telnet_options;
527 HttpReq httpreq;
529 /* for bandwidth limiting features: */
530 curl_off_t sendpersecond; /* send to peer */
531 curl_off_t recvpersecond; /* receive from peer */
532 struct timeval lastsendtime;
533 size_t lastsendsize;
534 struct timeval lastrecvtime;
535 size_t lastrecvsize;
536 bool ftp_ssl;
537 bool ftp_ssl_reqd;
538 bool ftp_ssl_control;
539 bool ftp_ssl_ccc;
540 int ftp_ssl_ccc_mode;
542 char *socksproxy; /* set to server string */
543 int socksver; /* set to CURLPROXY_SOCKS* define */
545 bool tcp_nodelay;
546 long req_retry; /* number of retries */
547 long retry_delay; /* delay between retries (in seconds) */
548 long retry_maxtime; /* maximum time to keep retrying */
550 char *ftp_account; /* for ACCT */
551 char *ftp_alternative_to_user; /* send command if USER/PASS fails */
552 int ftp_filemethod;
554 bool ignorecl; /* --ignore-content-length */
555 bool disable_sessionid;
557 char *libcurl; /* output libcurl code to this file name */
558 bool raw;
559 bool post301;
560 bool nokeepalive; /* for keepalive needs */
561 long alivetime;
563 int default_node_flags; /* default flags to seach for each 'node', which is
564 basically each given URL to transfer */
565 struct OutStruct *outs;
568 #define WARN_PREFIX "Warning: "
569 #define WARN_TEXTWIDTH (79 - (int)strlen(WARN_PREFIX))
570 /* produce this text message to the user unless mute was selected */
571 static void warnf(struct Configurable *config, const char *fmt, ...)
573 if(!config->mute) {
574 va_list ap;
575 int len;
576 char *ptr;
577 char print_buffer[256];
579 va_start(ap, fmt);
580 va_start(ap, fmt);
581 len = vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
582 va_end(ap);
584 ptr = print_buffer;
585 while(len > 0) {
586 fputs(WARN_PREFIX, config->errors);
588 if(len > (int)WARN_TEXTWIDTH) {
589 int cut = WARN_TEXTWIDTH-1;
591 while(!ISSPACE(ptr[cut]) && cut) {
592 cut--;
594 if(0 == cut)
595 /* not a single cutting position was found, just cut it at the
596 max text width then! */
597 cut = WARN_TEXTWIDTH-1;
599 fwrite(ptr, cut + 1, 1, config->errors);
600 fputs("\n", config->errors);
601 ptr += cut+1; /* skip the space too */
602 len -= cut;
604 else {
605 fputs(ptr, config->errors);
606 len = 0;
613 * This is the main global constructor for the app. Call this before
614 * _any_ libcurl usage. If this fails, *NO* libcurl functions may be
615 * used, or havoc may be the result.
617 static CURLcode main_init(void)
619 #ifdef DJGPP
620 /* stop stat() wasting time */
621 _djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
622 #endif
623 return curl_global_init(CURL_GLOBAL_DEFAULT);
627 * This is the main global destructor for the app. Call this after
628 * _all_ libcurl usage is done.
630 static void main_free(void)
632 curl_global_cleanup();
633 #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
634 /* close iconv conversion descriptor */
635 if(inbound_cd != (iconv_t)-1)
636 iconv_close(inbound_cd);
637 if(outbound_cd != (iconv_t)-1)
638 iconv_close(outbound_cd);
639 #endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
642 static int SetHTTPrequest(struct Configurable *config,
643 HttpReq req, HttpReq *store)
645 if((*store == HTTPREQ_UNSPEC) ||
646 (*store == req)) {
647 *store = req;
648 return 0;
650 warnf(config, "You can only select one HTTP request!\n");
651 return 1;
654 static void helpf(FILE *errors, const char *fmt, ...)
656 va_list ap;
657 if(fmt) {
658 va_start(ap, fmt);
659 fputs("curl: ", errors); /* prefix it */
660 vfprintf(errors, fmt, ap);
661 va_end(ap);
663 fprintf(errors, "curl: try 'curl --help' "
664 #ifdef USE_MANUAL
665 "or 'curl --manual' "
666 #endif
667 "for more information\n");
671 * A chain of these nodes contain URL to get and where to put the URL's
672 * contents.
674 struct getout {
675 struct getout *next; /* next one */
676 char *url; /* the URL we deal with */
677 char *outfile; /* where to store the output */
678 char *infile; /* file to upload, if GETOUT_UPLOAD is set */
679 int flags; /* options */
681 #define GETOUT_OUTFILE (1<<0) /* set when outfile is deemed done */
682 #define GETOUT_URL (1<<1) /* set when URL is deemed done */
683 #define GETOUT_USEREMOTE (1<<2) /* use remote file name locally */
684 #define GETOUT_UPLOAD (1<<3) /* if set, -T has been used */
685 #define GETOUT_NOUPLOAD (1<<4) /* if set, -T "" has been used */
687 static void help(void)
689 int i;
690 /* A few of these source lines are >80 columns wide, but that's only because
691 breaking the strings narrower makes this chunk look even worse!
693 Starting with 7.18.0, this list of command line options is sorted based
694 on the long option name. It is not done automatically, although a command
695 line like the following can help out:
697 curl --help | cut -c5- | grep "^-" | sort
699 static const char * const helptext[]={
700 "Usage: curl [options...] <url>",
701 "Options: (H) means HTTP/HTTPS only, (F) means FTP only",
702 " --anyauth Pick \"any\" authentication method (H)",
703 " -a/--append Append to target file when uploading (F/SFTP)",
704 " --basic Use HTTP Basic Authentication (H)",
705 " --cacert <file> CA certificate to verify peer against (SSL)",
706 " --capath <directory> CA directory to verify peer against (SSL)",
707 " -E/--cert <cert[:passwd]> Client certificate file and password (SSL)",
708 " --cert-type <type> Certificate file type (DER/PEM/ENG) (SSL)",
709 " --ciphers <list> SSL ciphers to use (SSL)",
710 " --compressed Request compressed response (using deflate or gzip)",
711 " -K/--config <file> Specify which config file to read",
712 " --connect-timeout <seconds> Maximum time allowed for connection",
713 " -C/--continue-at <offset> Resumed transfer offset",
714 " -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)",
715 " -c/--cookie-jar <file> Write cookies to this file after operation (H)",
716 " --create-dirs Create necessary local directory hierarchy",
717 " --crlf Convert LF to CRLF in upload",
718 " -d/--data <data> HTTP POST data (H)",
719 " --data-ascii <data> HTTP POST ASCII data (H)",
720 " --data-binary <data> HTTP POST binary data (H)",
721 " --data-urlencode <name=data/name@filename> HTTP POST data url encoded (H)",
722 " --digest Use HTTP Digest Authentication (H)",
723 " --disable-eprt Inhibit using EPRT or LPRT (F)",
724 " --disable-epsv Inhibit using EPSV (F)",
725 " -D/--dump-header <file> Write the headers to this file",
726 " --egd-file <file> EGD socket path for random data (SSL)",
727 " --engine <eng> Crypto engine to use (SSL). \"--engine list\" for list",
728 #ifdef USE_ENVIRONMENT
729 " --environment Write results to environment variables (RISC OS)",
730 #endif
731 " -f/--fail Fail silently (no output at all) on HTTP errors (H)",
732 " -F/--form <name=content> Specify HTTP multipart POST data (H)",
733 " --form-string <name=string> Specify HTTP multipart POST data (H)",
734 " --ftp-account <data> Account data to send when requested by server (F)",
735 " --ftp-alternative-to-user <cmd> String to replace \"USER [name]\" (F)",
736 " --ftp-create-dirs Create the remote dirs if not present (F)",
737 " --ftp-method [multicwd/nocwd/singlecwd] Control CWD usage (F)",
738 " --ftp-pasv Use PASV/EPSV instead of PORT (F)",
739 " -P/--ftp-port <address> Use PORT with address instead of PASV (F)",
740 " --ftp-skip-pasv-ip Skip the IP address for PASV (F)\n"
741 " --ftp-ssl Try SSL/TLS for ftp transfer (F)",
742 " --ftp-ssl-ccc Send CCC after authenticating (F)",
743 " --ftp-ssl-ccc-mode [active/passive] Set CCC mode (F)",
744 " --ftp-ssl-control Require SSL/TLS for ftp login, clear for transfer (F)",
745 " --ftp-ssl-reqd Require SSL/TLS for ftp transfer (F)",
746 " -G/--get Send the -d data with a HTTP GET (H)",
747 " -g/--globoff Disable URL sequences and ranges using {} and []",
748 " -H/--header <line> Custom header to pass to server (H)",
749 " -I/--head Show document info only",
750 " -h/--help This help text",
751 " --hostpubmd5 <md5> Hex encoded MD5 string of the host public key. (SSH)",
752 " -0/--http1.0 Use HTTP 1.0 (H)",
753 " --ignore-content-length Ignore the HTTP Content-Length header",
754 " -i/--include Include protocol headers in the output (H/F)",
755 " -k/--insecure Allow connections to SSL sites without certs (H)",
756 " --interface <interface> Specify network interface/address to use",
757 " -4/--ipv4 Resolve name to IPv4 address",
758 " -6/--ipv6 Resolve name to IPv6 address",
759 " -j/--junk-session-cookies Ignore session cookies read from file (H)",
760 " --keepalive-time <seconds> Interval between keepalive probes",
761 " --key <key> Private key file name (SSL/SSH)",
762 " --key-type <type> Private key file type (DER/PEM/ENG) (SSL)",
763 " --krb <level> Enable Kerberos with specified security level (F)",
764 " --libcurl <file> Dump libcurl equivalent code of this command line",
765 " --limit-rate <rate> Limit transfer speed to this rate",
766 " -l/--list-only List only names of an FTP directory (F)",
767 " --local-port <num>[-num] Force use of these local port numbers",
768 " -L/--location Follow Location: hints (H)",
769 " --location-trusted Follow Location: and send auth to other hosts (H)",
770 " -M/--manual Display the full manual",
771 " --max-filesize <bytes> Maximum file size to download (H/F)",
772 " --max-redirs <num> Maximum number of redirects allowed (H)",
773 " -m/--max-time <seconds> Maximum time allowed for the transfer",
774 " --negotiate Use HTTP Negotiate Authentication (H)",
775 " -n/--netrc Must read .netrc for user name and password",
776 " --netrc-optional Use either .netrc or URL; overrides -n",
777 " -N/--no-buffer Disable buffering of the output stream",
778 " --no-keepalive Disable keepalive use on the connection",
779 " --no-sessionid Disable SSL session-ID reusing (SSL)",
780 " --ntlm Use HTTP NTLM authentication (H)",
781 " -o/--output <file> Write output to <file> instead of stdout",
782 " --pass <pass> Pass phrase for the private key (SSL/SSH)",
783 " --post301 Do not switch to GET after following a 301 redirect (H)",
784 " -#/--progress-bar Display transfer progress as a progress bar",
785 " -x/--proxy <host[:port]> Use HTTP proxy on given port",
786 " --proxy-anyauth Pick \"any\" proxy authentication method (H)",
787 " --proxy-basic Use Basic authentication on the proxy (H)",
788 " --proxy-digest Use Digest authentication on the proxy (H)",
789 " --proxy-negotiate Use Negotiate authentication on the proxy (H)",
790 " --proxy-ntlm Use NTLM authentication on the proxy (H)",
791 " -U/--proxy-user <user[:password]> Set proxy user and password",
792 " -p/--proxytunnel Operate through a HTTP proxy tunnel (using CONNECT)",
793 " --pubkey <key> Public key file name (SSH)",
794 " -Q/--quote <cmd> Send command(s) to server before file transfer (F/SFTP)",
795 " --random-file <file> File for reading random data from (SSL)",
796 " -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server",
797 " --raw Pass HTTP \"raw\", without any transfer decoding (H)",
798 " -e/--referer Referer URL (H)",
799 " -O/--remote-name Write output to a file named as the remote file",
800 " --remote-name-all Use the remote file name for all URLs",
801 " -R/--remote-time Set the remote file's time on the local output",
802 " -X/--request <command> Specify request command to use",
803 " --retry <num> Retry request <num> times if transient problems occur",
804 " --retry-delay <seconds> When retrying, wait this many seconds between each",
805 " --retry-max-time <seconds> Retry only within this period",
806 " -S/--show-error Show error. With -s, make curl show errors when they occur",
807 " -s/--silent Silent mode. Don't output anything",
808 " --socks4 <host[:port]> SOCKS4 proxy on given host + port",
809 " --socks4a <host[:port]> SOCKS4a proxy on given host + port",
810 " --socks5 <host[:port]> SOCKS5 proxy on given host + port",
811 " --socks5-hostname <host[:port]> SOCKS5 proxy, pass host name to proxy",
812 " -Y/--speed-limit Stop transfer if below speed-limit for 'speed-time' secs",
813 " -y/--speed-time Time needed to trig speed-limit abort. Defaults to 30",
814 " -2/--sslv2 Use SSLv2 (SSL)",
815 " -3/--sslv3 Use SSLv3 (SSL)",
816 " --stderr <file> Where to redirect stderr. - means stdout",
817 " --tcp-nodelay Use the TCP_NODELAY option",
818 " -t/--telnet-option <OPT=val> Set telnet option",
819 " -z/--time-cond <time> Transfer based on a time condition",
820 " -1/--tlsv1 Use TLSv1 (SSL)",
821 " --trace <file> Write a debug trace to the given file",
822 " --trace-ascii <file> Like --trace but without the hex output",
823 " --trace-time Add time stamps to trace/verbose output",
824 " -T/--upload-file <file> Transfer <file> to remote site",
825 " --url <URL> Set URL to work with",
826 " -B/--use-ascii Use ASCII/text transfer",
827 " -u/--user <user[:password]> Set server user and password",
828 " -A/--user-agent <string> User-Agent to send to server (H)",
829 " -v/--verbose Make the operation more talkative",
830 " -V/--version Show version number and quit",
832 #ifdef MSDOS
833 " --wdebug Turn on Watt-32 debugging under DJGPP",
834 #endif
835 " -w/--write-out <format> What to output after completion",
836 " -q If used as the first parameter disables .curlrc",
837 NULL
839 for(i=0; helptext[i]; i++) {
840 puts(helptext[i]);
841 #ifdef PRINT_LINES_PAUSE
842 if (i && ((i % PRINT_LINES_PAUSE) == 0))
843 pressanykey();
844 #endif
848 struct LongShort {
849 const char *letter;
850 const char *lname;
851 bool extraparam;
854 /* global variable to hold info about libcurl */
855 static curl_version_info_data *curlinfo;
857 static int parseconfig(const char *filename,
858 struct Configurable *config);
859 static char *my_get_line(FILE *fp);
860 static int create_dir_hierarchy(const char *outfile, FILE *errors);
862 static void GetStr(char **string,
863 const char *value)
865 if(*string)
866 free(*string);
867 if(value)
868 *string = strdup(value);
869 else
870 *string = NULL;
873 static void clean_getout(struct Configurable *config)
875 struct getout *node=config->url_list;
876 struct getout *next;
878 while(node) {
879 next = node->next;
880 if(node->url)
881 free(node->url);
882 if(node->outfile)
883 free(node->outfile);
884 if(node->infile)
885 free(node->infile);
886 free(node);
888 node = next; /* GOTO next */
892 static struct getout *new_getout(struct Configurable *config)
894 struct getout *node =malloc(sizeof(struct getout));
895 struct getout *last= config->url_last;
896 if(node) {
897 /* clear the struct */
898 memset(node, 0, sizeof(struct getout));
900 /* append this new node last in the list */
901 if(last)
902 last->next = node;
903 else
904 config->url_list = node; /* first node */
906 /* move the last pointer */
907 config->url_last = node;
909 node->flags = config->default_node_flags;
911 return node;
914 /* Structure for storing the information needed to build a multiple files
915 * section
917 struct multi_files {
918 struct curl_forms form;
919 struct multi_files *next;
922 /* Add a new list entry possibly with a type_name
924 static struct multi_files *
925 AddMultiFiles (const char *file_name,
926 const char *type_name,
927 const char *show_filename,
928 struct multi_files **multi_start,
929 struct multi_files **multi_current)
931 struct multi_files *multi;
932 struct multi_files *multi_type = NULL;
933 struct multi_files *multi_name = NULL;
934 multi = (struct multi_files *)malloc(sizeof(struct multi_files));
935 if (multi) {
936 memset(multi, 0, sizeof(struct multi_files));
937 multi->form.option = CURLFORM_FILE;
938 multi->form.value = file_name;
940 else
941 return NULL;
943 if (!*multi_start)
944 *multi_start = multi;
946 if (type_name) {
947 multi_type = (struct multi_files *)malloc(sizeof(struct multi_files));
948 if (multi_type) {
949 memset(multi_type, 0, sizeof(struct multi_files));
950 multi_type->form.option = CURLFORM_CONTENTTYPE;
951 multi_type->form.value = type_name;
952 multi->next = multi_type;
954 multi = multi_type;
956 else {
957 free (multi);
958 return NULL;
961 if (show_filename) {
962 multi_name = (struct multi_files *)malloc(sizeof(struct multi_files));
963 if (multi_name) {
964 memset(multi_name, 0, sizeof(struct multi_files));
965 multi_name->form.option = CURLFORM_FILENAME;
966 multi_name->form.value = show_filename;
967 multi->next = multi_name;
969 multi = multi_name;
971 else {
972 free (multi);
973 return NULL;
977 if (*multi_current)
978 (*multi_current)->next = multi;
980 *multi_current = multi;
982 return *multi_current;
985 /* Free the items of the list.
987 static void FreeMultiInfo (struct multi_files *multi_start)
989 struct multi_files *multi;
990 while (multi_start) {
991 multi = multi_start;
992 multi_start = multi_start->next;
993 free (multi);
997 /* Print list of OpenSSL engines supported.
999 static void list_engines (const struct curl_slist *engines)
1001 puts ("Build-time engines:");
1002 if (!engines) {
1003 puts (" <none>");
1004 return;
1006 for ( ; engines; engines = engines->next)
1007 printf (" %s\n", engines->data);
1010 /***************************************************************************
1012 * formparse()
1014 * Reads a 'name=value' parameter and builds the appropriate linked list.
1016 * Specify files to upload with 'name=@filename'. Supports specified
1017 * given Content-Type of the files. Such as ';type=<content-type>'.
1019 * If literal_value is set, any initial '@' or '<' in the value string
1020 * loses its special meaning, as does any embedded ';type='.
1022 * You may specify more than one file for a single name (field). Specify
1023 * multiple files by writing it like:
1025 * 'name=@filename,filename2,filename3'
1027 * If you want content-types specified for each too, write them like:
1029 * 'name=@filename;type=image/gif,filename2,filename3'
1031 * If you want custom headers added for a single part, write them in a separate
1032 * file and do like this:
1034 * 'name=foo;headers=@headerfile' or why not
1035 * 'name=@filemame;headers=@headerfile'
1037 * To upload a file, but to fake the file name that will be included in the
1038 * formpost, do like this:
1040 * 'name=@filename;filename=/dev/null'
1042 * This function uses curl_formadd to fulfill it's job. Is heavily based on
1043 * the old curl_formparse code.
1045 ***************************************************************************/
1047 #define FORM_FILE_SEPARATOR ','
1048 #define FORM_TYPE_SEPARATOR ';'
1050 static int formparse(struct Configurable *config,
1051 const char *input,
1052 struct curl_httppost **httppost,
1053 struct curl_httppost **last_post,
1054 bool literal_value)
1056 /* nextarg MUST be a string in the format 'name=contents' and we'll
1057 build a linked list with the info */
1058 char name[256];
1059 char *contents;
1060 char major[128];
1061 char minor[128];
1062 char *contp;
1063 const char *type = NULL;
1064 char *sep;
1065 char *sep2;
1067 if((1 == sscanf(input, "%255[^=]=", name)) &&
1068 (contp = strchr(input, '='))) {
1069 /* the input was using the correct format */
1071 /* Allocate the contents */
1072 contents = strdup(contp+1);
1073 if(!contents) {
1074 fprintf(config->errors, "out of memory\n");
1075 return 1;
1077 contp = contents;
1079 if('@' == contp[0] && !literal_value) {
1080 struct multi_files *multi_start = NULL, *multi_current = NULL;
1081 /* we use the @-letter to indicate file name(s) */
1082 contp++;
1084 multi_start = multi_current=NULL;
1086 do {
1087 /* since this was a file, it may have a content-type specifier
1088 at the end too, or a filename. Or both. */
1089 char *ptr;
1090 char *filename=NULL;
1092 sep=strchr(contp, FORM_TYPE_SEPARATOR);
1093 sep2=strchr(contp, FORM_FILE_SEPARATOR);
1095 /* pick the closest */
1096 if(sep2 && (sep2 < sep)) {
1097 sep = sep2;
1099 /* no type was specified! */
1102 type = NULL;
1104 if(sep) {
1106 /* if we got here on a comma, don't do much */
1107 if(FORM_FILE_SEPARATOR == *sep)
1108 ptr = NULL;
1109 else
1110 ptr = sep+1;
1112 *sep=0; /* terminate file name at separator */
1114 while(ptr && (FORM_FILE_SEPARATOR!= *ptr)) {
1116 /* pass all white spaces */
1117 while(ISSPACE(*ptr))
1118 ptr++;
1120 if(curlx_strnequal("type=", ptr, 5)) {
1121 /* set type pointer */
1122 type = &ptr[5];
1124 /* verify that this is a fine type specifier */
1125 if(2 != sscanf(type, "%127[^/]/%127[^;,\n]",
1126 major, minor)) {
1127 warnf(config, "Illegally formatted content-type field!\n");
1128 free(contents);
1129 FreeMultiInfo (multi_start);
1130 return 2; /* illegal content-type syntax! */
1132 /* now point beyond the content-type specifier */
1133 sep = (char *)type + strlen(major)+strlen(minor)+1;
1135 if(*sep) {
1136 *sep=0; /* zero terminate type string */
1138 ptr=sep+1;
1140 else
1141 ptr = NULL; /* end */
1143 else if(curlx_strnequal("filename=", ptr, 9)) {
1144 filename = &ptr[9];
1145 ptr=strchr(filename, FORM_TYPE_SEPARATOR);
1146 if(!ptr) {
1147 ptr=strchr(filename, FORM_FILE_SEPARATOR);
1149 if(ptr) {
1150 *ptr=0; /* zero terminate */
1151 ptr++;
1154 else
1155 /* confusion, bail out of loop */
1156 break;
1158 /* find the following comma */
1159 if(ptr)
1160 sep=strchr(ptr, FORM_FILE_SEPARATOR);
1161 else
1162 sep=NULL;
1164 else {
1165 sep=strchr(contp, FORM_FILE_SEPARATOR);
1167 if(sep) {
1168 /* the next file name starts here */
1169 *sep =0;
1170 sep++;
1172 /* if type == NULL curl_formadd takes care of the problem */
1174 if (!AddMultiFiles (contp, type, filename, &multi_start,
1175 &multi_current)) {
1176 warnf(config, "Error building form post!\n");
1177 free(contents);
1178 FreeMultiInfo (multi_start);
1179 return 3;
1181 contp = sep; /* move the contents pointer to after the separator */
1183 } while(sep && *sep); /* loop if there's another file name */
1185 /* now we add the multiple files section */
1186 if (multi_start) {
1187 struct curl_forms *forms = NULL;
1188 struct multi_files *ptr = multi_start;
1189 unsigned int i, count = 0;
1190 while (ptr) {
1191 ptr = ptr->next;
1192 ++count;
1194 forms =
1195 (struct curl_forms *)malloc((count+1)*sizeof(struct curl_forms));
1196 if (!forms)
1198 fprintf(config->errors, "Error building form post!\n");
1199 free(contents);
1200 FreeMultiInfo (multi_start);
1201 return 4;
1203 for (i = 0, ptr = multi_start; i < count; ++i, ptr = ptr->next)
1205 forms[i].option = ptr->form.option;
1206 forms[i].value = ptr->form.value;
1208 forms[count].option = CURLFORM_END;
1209 FreeMultiInfo (multi_start);
1210 if (curl_formadd(httppost, last_post,
1211 CURLFORM_COPYNAME, name,
1212 CURLFORM_ARRAY, forms, CURLFORM_END) != 0) {
1213 warnf(config, "curl_formadd failed!\n");
1214 free(forms);
1215 free(contents);
1216 return 5;
1218 free(forms);
1221 else {
1222 struct curl_forms info[4];
1223 int i = 0;
1224 char *ct = literal_value? NULL: strstr(contp, ";type=");
1226 info[i].option = CURLFORM_COPYNAME;
1227 info[i].value = name;
1228 i++;
1230 if(ct) {
1231 info[i].option = CURLFORM_CONTENTTYPE;
1232 info[i].value = &ct[6];
1233 i++;
1234 ct[0]=0; /* zero terminate here */
1237 if( contp[0]=='<' && !literal_value) {
1238 info[i].option = CURLFORM_FILECONTENT;
1239 info[i].value = contp+1;
1240 i++;
1241 info[i].option = CURLFORM_END;
1243 if (curl_formadd(httppost, last_post,
1244 CURLFORM_ARRAY, info, CURLFORM_END ) != 0) {
1245 warnf(config, "curl_formadd failed, possibly the file %s is bad!\n",
1246 contp+1);
1247 free(contents);
1248 return 6;
1251 else {
1252 #ifdef CURL_DOES_CONVERSIONS
1253 convert_to_network(contp, strlen(contp));
1254 #endif
1255 info[i].option = CURLFORM_COPYCONTENTS;
1256 info[i].value = contp;
1257 i++;
1258 info[i].option = CURLFORM_END;
1259 if (curl_formadd(httppost, last_post,
1260 CURLFORM_ARRAY, info, CURLFORM_END) != 0) {
1261 warnf(config, "curl_formadd failed!\n");
1262 free(contents);
1263 return 7;
1269 else {
1270 warnf(config, "Illegally formatted input field!\n");
1271 return 1;
1273 free(contents);
1274 return 0;
1278 typedef enum {
1279 PARAM_OK,
1280 PARAM_OPTION_AMBIGUOUS,
1281 PARAM_OPTION_UNKNOWN,
1282 PARAM_REQUIRES_PARAMETER,
1283 PARAM_BAD_USE,
1284 PARAM_HELP_REQUESTED,
1285 PARAM_GOT_EXTRA_PARAMETER,
1286 PARAM_BAD_NUMERIC,
1287 PARAM_LIBCURL_DOESNT_SUPPORT,
1288 PARAM_NO_MEM,
1289 PARAM_LAST
1290 } ParameterError;
1292 static const char *param2text(int res)
1294 ParameterError error = (ParameterError)res;
1295 switch(error) {
1296 case PARAM_GOT_EXTRA_PARAMETER:
1297 return "had unsupported trailing garbage";
1298 case PARAM_OPTION_UNKNOWN:
1299 return "is unknown";
1300 case PARAM_OPTION_AMBIGUOUS:
1301 return "is ambiguous";
1302 case PARAM_REQUIRES_PARAMETER:
1303 return "requires parameter";
1304 case PARAM_BAD_USE:
1305 return "is badly used here";
1306 case PARAM_BAD_NUMERIC:
1307 return "expected a proper numerical parameter";
1308 case PARAM_LIBCURL_DOESNT_SUPPORT:
1309 return "the installed libcurl version doesn't support this";
1310 case PARAM_NO_MEM:
1311 return "out of memory";
1312 default:
1313 return "unknown error";
1317 static ParameterError file2string(char **bufp, FILE *file)
1319 char buffer[256];
1320 char *ptr;
1321 char *string = NULL;
1322 size_t stringlen = 0;
1323 size_t buflen;
1325 if(file) {
1326 while(fgets(buffer, sizeof(buffer), file)) {
1327 if((ptr = strchr(buffer, '\r')) != NULL)
1328 *ptr = '\0';
1329 if((ptr = strchr(buffer, '\n')) != NULL)
1330 *ptr = '\0';
1331 buflen = strlen(buffer);
1332 if((ptr = realloc(string, stringlen+buflen+1)) == NULL) {
1333 if(string)
1334 free(string);
1335 return PARAM_NO_MEM;
1337 string = ptr;
1338 strcpy(string+stringlen, buffer);
1339 stringlen += buflen;
1342 *bufp = string;
1343 return PARAM_OK;
1346 static ParameterError file2memory(char **bufp, size_t *size, FILE *file)
1348 char *newbuf;
1349 char *buffer = NULL;
1350 size_t alloc = 512;
1351 size_t nused = 0;
1352 size_t nread;
1354 if(file) {
1355 do {
1356 if(!buffer || (alloc == nused)) {
1357 /* size_t overflow detection for huge files */
1358 if(alloc+1 > ((size_t)-1)/2) {
1359 if(buffer)
1360 free(buffer);
1361 return PARAM_NO_MEM;
1363 alloc *= 2;
1364 /* allocate an extra char, reserved space, for null termination */
1365 if((newbuf = realloc(buffer, alloc+1)) == NULL) {
1366 if(buffer)
1367 free(buffer);
1368 return PARAM_NO_MEM;
1370 buffer = newbuf;
1372 nread = fread(buffer+nused, 1, alloc-nused, file);
1373 nused += nread;
1374 } while(nread);
1375 /* null terminate the buffer in case it's used as a string later */
1376 buffer[nused] = '\0';
1377 /* free trailing slack space, if possible */
1378 if(alloc != nused) {
1379 if((newbuf = realloc(buffer, nused+1)) != NULL)
1380 buffer = newbuf;
1382 /* discard buffer if nothing was read */
1383 if(!nused) {
1384 free(buffer);
1385 buffer = NULL; /* no string */
1388 *size = nused;
1389 *bufp = buffer;
1390 return PARAM_OK;
1393 static void cleanarg(char *str)
1395 #ifdef HAVE_WRITABLE_ARGV
1396 /* now that GetStr has copied the contents of nextarg, wipe the next
1397 * argument out so that the username:password isn't displayed in the
1398 * system process list */
1399 if (str) {
1400 size_t len = strlen(str);
1401 memset(str, ' ', len);
1403 #else
1404 (void)str;
1405 #endif
1409 * Parse the string and write the integer in the given address. Return
1410 * non-zero on failure, zero on success.
1412 * The string must start with a digit to be valid.
1414 * Since this function gets called with the 'nextarg' pointer from within the
1415 * getparameter a lot, we must check it for NULL before accessing the str
1416 * data.
1419 static int str2num(long *val, const char *str)
1421 int retcode = 0;
1422 if(str && ISDIGIT(*str))
1423 *val = atoi(str);
1424 else
1425 retcode = 1; /* badness */
1426 return retcode;
1430 * Parses the given string looking for an offset (which may be
1431 * a larger-than-integer value).
1433 * @param val the offset to populate
1434 * @param str the buffer containing the offset
1435 * @return zero if successful, non-zero if failure.
1437 static int str2offset(curl_off_t *val, const char *str)
1439 #if (CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
1440 *val = curlx_strtoofft(str, NULL, 0);
1441 if((*val == CURL_LLONG_MAX || *val == CURL_LLONG_MIN) && (ERRNO == ERANGE))
1442 return 1;
1443 #else
1444 *val = strtol(str, NULL, 0);
1445 if ((*val == LONG_MIN || *val == LONG_MAX) && ERRNO == ERANGE)
1446 return 1;
1447 #endif
1448 return 0;
1451 static void checkpasswd(const char *kind, /* for what purpose */
1452 char **userpwd) /* pointer to allocated string */
1454 char *ptr;
1455 if(!*userpwd)
1456 return;
1458 ptr = strchr(*userpwd, ':');
1459 if(!ptr) {
1460 /* no password present, prompt for one */
1461 char passwd[256]="";
1462 char prompt[256];
1463 size_t passwdlen;
1464 size_t userlen = strlen(*userpwd);
1465 char *passptr;
1467 /* build a nice-looking prompt */
1468 curlx_msnprintf(prompt, sizeof(prompt),
1469 "Enter %s password for user '%s':",
1470 kind, *userpwd);
1472 /* get password */
1473 getpass_r(prompt, passwd, sizeof(passwd));
1474 passwdlen = strlen(passwd);
1476 /* extend the allocated memory area to fit the password too */
1477 passptr = realloc(*userpwd,
1478 passwdlen + 1 + /* an extra for the colon */
1479 userlen + 1); /* an extra for the zero */
1481 if(passptr) {
1482 /* append the password separated with a colon */
1483 passptr[userlen]=':';
1484 memcpy(&passptr[userlen+1], passwd, passwdlen+1);
1485 *userpwd = passptr;
1490 static ParameterError add2list(struct curl_slist **list,
1491 const char *ptr)
1493 struct curl_slist *newlist = curl_slist_append(*list, ptr);
1494 if(newlist)
1495 *list = newlist;
1496 else
1497 return PARAM_NO_MEM;
1499 return PARAM_OK;
1502 static int ftpfilemethod(struct Configurable *config, const char *str)
1504 if(curlx_strequal("singlecwd", str))
1505 return CURLFTPMETHOD_SINGLECWD;
1506 if(curlx_strequal("nocwd", str))
1507 return CURLFTPMETHOD_NOCWD;
1508 if(curlx_strequal("multicwd", str))
1509 return CURLFTPMETHOD_MULTICWD;
1510 warnf(config, "unrecognized ftp file method '%s', using default\n", str);
1511 return CURLFTPMETHOD_MULTICWD;
1514 static int ftpcccmethod(struct Configurable *config, const char *str)
1516 if(curlx_strequal("passive", str))
1517 return CURLFTPSSL_CCC_PASSIVE;
1518 if(curlx_strequal("active", str))
1519 return CURLFTPSSL_CCC_ACTIVE;
1520 warnf(config, "unrecognized ftp CCC method '%s', using default\n", str);
1521 return CURLFTPSSL_CCC_PASSIVE;
1525 static int sockoptcallback(void *clientp, curl_socket_t curlfd,
1526 curlsocktype purpose)
1528 struct Configurable *config = (struct Configurable *)clientp;
1529 int onoff = 1; /* this callback is only used if we ask for keepalives on the
1530 connection */
1531 #if defined(TCP_KEEPIDLE) || defined(TCP_KEEPINTVL)
1532 int keepidle = (int)config->alivetime;
1533 #endif
1535 switch (purpose) {
1536 case CURLSOCKTYPE_IPCXN:
1537 if(setsockopt(curlfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&onoff,
1538 sizeof(onoff)) < 0) {
1539 /* don't abort operation, just issue a warning */
1540 SET_SOCKERRNO(0);
1541 warnf(clientp, "Could not set SO_KEEPALIVE!\n");
1542 return 0;
1544 else {
1545 if (config->alivetime) {
1546 #ifdef TCP_KEEPIDLE
1547 if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&keepidle,
1548 sizeof(keepidle)) < 0) {
1549 /* don't abort operation, just issue a warning */
1550 SET_SOCKERRNO(0);
1551 warnf(clientp, "Could not set TCP_KEEPIDLE!\n");
1552 return 0;
1554 #endif
1555 #ifdef TCP_KEEPINTVL
1556 if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&keepidle,
1557 sizeof(keepidle)) < 0) {
1558 /* don't abort operation, just issue a warning */
1559 SET_SOCKERRNO(0);
1560 warnf(clientp, "Could not set TCP_KEEPINTVL!\n");
1561 return 0;
1563 #endif
1566 break;
1567 default:
1568 break;
1571 return 0;
1575 static ParameterError getparameter(char *flag, /* f or -long-flag */
1576 char *nextarg, /* NULL if unset */
1577 bool *usedarg, /* set to TRUE if the arg
1578 has been used */
1579 struct Configurable *config)
1581 char letter;
1582 char subletter=0; /* subletters can only occur on long options */
1583 int rc; /* generic return code variable */
1584 const char *parse=NULL;
1585 unsigned int j;
1586 time_t now;
1587 int hit=-1;
1588 bool longopt=FALSE;
1589 bool singleopt=FALSE; /* when true means '-o foo' used '-ofoo' */
1590 ParameterError err;
1591 bool toggle=TRUE; /* how to switch boolean options, on or off. Controlled
1592 by using --OPTION or --no-OPTION */
1594 /* single-letter,
1595 long-name,
1596 boolean whether it takes an additional argument
1598 static const struct LongShort aliases[]= {
1599 /* all these ones, starting with "*" or "$" as a short-option have *no*
1600 short option to mention. */
1601 {"*", "url", TRUE},
1602 {"*a", "random-file", TRUE},
1603 {"*b", "egd-file", TRUE},
1604 {"*c", "connect-timeout", TRUE},
1605 {"*d", "ciphers", TRUE},
1606 {"*e", "disable-epsv", FALSE},
1607 {"*E", "epsv", FALSE}, /* made like this to make --no-epsv and --epsv to
1608 work although --disable-epsv is the documented
1609 option */
1610 #ifdef USE_ENVIRONMENT
1611 {"*f", "environment", FALSE},
1612 #endif
1613 {"*g", "trace", TRUE},
1614 {"*h", "trace-ascii", TRUE},
1615 {"*i", "limit-rate", TRUE},
1616 {"*j", "compressed", FALSE}, /* might take an arg someday */
1617 {"*k", "digest", FALSE},
1618 {"*l", "negotiate", FALSE},
1619 {"*m", "ntlm", FALSE},
1620 {"*n", "basic", FALSE},
1621 {"*o", "anyauth", FALSE},
1622 #ifdef MSDOS
1623 {"*p", "wdebug", FALSE},
1624 #endif
1625 {"*q", "ftp-create-dirs", FALSE},
1626 {"*r", "create-dirs", FALSE},
1627 {"*s", "max-redirs", TRUE},
1628 {"*t", "proxy-ntlm", FALSE},
1629 {"*u", "crlf", FALSE},
1630 {"*v", "stderr", TRUE},
1631 {"*w", "interface", TRUE},
1632 {"*x", "krb" , TRUE},
1633 {"*x", "krb4" , TRUE}, /* this is the previous name */
1634 {"*y", "max-filesize", TRUE},
1635 {"*z", "disable-eprt", FALSE},
1636 {"*Z", "eprt", FALSE}, /* made like this to make --no-eprt and --eprt to
1637 work although --disable-eprt is the documented
1638 option */
1639 {"$a", "ftp-ssl", FALSE},
1640 {"$b", "ftp-pasv", FALSE},
1641 {"$c", "socks5", TRUE},
1642 {"$c", "socks", TRUE}, /* this is how the option once was documented
1643 but we prefer the --socks5 version for
1644 explicit version */
1645 {"$d", "tcp-nodelay",FALSE},
1646 {"$e", "proxy-digest", FALSE},
1647 {"$f", "proxy-basic", FALSE},
1648 {"$g", "retry", TRUE},
1649 {"$h", "retry-delay", TRUE},
1650 {"$i", "retry-max-time", TRUE},
1651 {"$k", "proxy-negotiate", FALSE},
1652 {"$m", "ftp-account", TRUE},
1653 {"$n", "proxy-anyauth", FALSE},
1654 {"$o", "trace-time", FALSE},
1655 {"$p", "ignore-content-length", FALSE},
1656 {"$q", "ftp-skip-pasv-ip", FALSE},
1657 {"$r", "ftp-method", TRUE},
1658 {"$s", "local-port", TRUE},
1659 {"$t", "socks4", TRUE},
1660 {"$T", "socks4a", TRUE},
1661 {"$u", "ftp-alternative-to-user", TRUE},
1662 {"$v", "ftp-ssl-reqd", FALSE},
1663 {"$w", "sessionid", FALSE}, /* listed as --no-sessionid in the help */
1664 {"$x", "ftp-ssl-control", FALSE},
1665 {"$y", "ftp-ssl-ccc", FALSE},
1666 {"$j", "ftp-ssl-ccc-mode", TRUE},
1667 {"$z", "libcurl", TRUE},
1668 {"$#", "raw", FALSE},
1669 {"$0", "post301", FALSE},
1670 {"$1", "keepalive", FALSE}, /* listed as --no-keepalive in the help */
1671 {"$2", "socks5-hostname", TRUE},
1672 {"$3", "keepalive-time", TRUE},
1674 {"0", "http1.0", FALSE},
1675 {"1", "tlsv1", FALSE},
1676 {"2", "sslv2", FALSE},
1677 {"3", "sslv3", FALSE},
1678 {"4", "ipv4", FALSE},
1679 {"6", "ipv6", FALSE},
1680 {"a", "append", FALSE},
1681 {"A", "user-agent", TRUE},
1682 {"b", "cookie", TRUE},
1683 {"B", "use-ascii", FALSE},
1684 {"c", "cookie-jar", TRUE},
1685 {"C", "continue-at", TRUE},
1686 {"d", "data", TRUE},
1687 {"da", "data-ascii", TRUE},
1688 {"db", "data-binary", TRUE},
1689 {"de", "data-urlencode", TRUE},
1690 {"D", "dump-header", TRUE},
1691 {"e", "referer", TRUE},
1692 {"E", "cert", TRUE},
1693 {"Ea", "cacert", TRUE},
1694 {"Eb","cert-type", TRUE},
1695 {"Ec","key", TRUE},
1696 {"Ed","key-type", TRUE},
1697 {"Ee","pass", TRUE},
1698 {"Ef","engine", TRUE},
1699 {"Eg","capath ", TRUE},
1700 {"Eh","pubkey", TRUE},
1701 {"Ei", "hostpubmd5", TRUE},
1702 {"f", "fail", FALSE},
1703 {"F", "form", TRUE},
1704 {"Fs","form-string", TRUE},
1705 {"g", "globoff", FALSE},
1706 {"G", "get", FALSE},
1707 {"h", "help", FALSE},
1708 {"H", "header", TRUE},
1709 {"i", "include", FALSE},
1710 {"I", "head", FALSE},
1711 {"j", "junk-session-cookies", FALSE},
1712 {"k", "insecure", FALSE},
1713 {"K", "config", TRUE},
1714 {"l", "list-only", FALSE},
1715 {"L", "location", FALSE},
1716 {"Lt", "location-trusted", FALSE},
1717 {"m", "max-time", TRUE},
1718 {"M", "manual", FALSE},
1719 {"n", "netrc", FALSE},
1720 {"no", "netrc-optional", FALSE},
1721 {"N", "buffer", FALSE}, /* listed as --no-buffer in the help */
1722 {"o", "output", TRUE},
1723 {"O", "remote-name", FALSE},
1724 {"Oa", "remote-name-all", FALSE},
1725 {"p", "proxytunnel", FALSE},
1726 {"P", "ftpport", TRUE}, /* older version */
1727 {"P", "ftp-port", TRUE},
1728 {"q", "disable", FALSE},
1729 {"Q", "quote", TRUE},
1730 {"r", "range", TRUE},
1731 {"R", "remote-time", FALSE},
1732 {"s", "silent", FALSE},
1733 {"S", "show-error", FALSE},
1734 {"t", "telnet-options", TRUE}, /* this is documented as telnet-option */
1735 {"T", "upload-file", TRUE},
1736 {"u", "user", TRUE},
1737 {"U", "proxy-user", TRUE},
1738 {"v", "verbose", FALSE},
1739 {"V", "version", FALSE},
1740 {"w", "write-out", TRUE},
1741 {"x", "proxy", TRUE},
1742 {"X", "request", TRUE},
1743 {"X", "http-request", TRUE}, /* OBSOLETE VERSION */
1744 {"Y", "speed-limit", TRUE},
1745 {"y", "speed-time", TRUE},
1746 {"z", "time-cond", TRUE},
1747 {"#", "progress-bar",FALSE},
1750 if(('-' != flag[0]) ||
1751 (('-' == flag[0]) && ('-' == flag[1]))) {
1752 /* this should be a long name */
1753 char *word=('-' == flag[0])?flag+2:flag;
1754 size_t fnam=strlen(word);
1755 int numhits=0;
1757 if(!strncmp(word, "no-", 3)) {
1758 /* disable this option but ignore the "no-" part when looking for it */
1759 word += 3;
1760 toggle = FALSE;
1763 for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
1764 if(curlx_strnequal(aliases[j].lname, word, fnam)) {
1765 longopt = TRUE;
1766 numhits++;
1767 if(curlx_strequal(aliases[j].lname, word)) {
1768 parse = aliases[j].letter;
1769 hit = j;
1770 numhits = 1; /* a single unique hit */
1771 break;
1773 parse = aliases[j].letter;
1774 hit = j;
1777 if(numhits>1) {
1778 /* this is at least the second match! */
1779 return PARAM_OPTION_AMBIGUOUS;
1781 if(hit < 0) {
1782 return PARAM_OPTION_UNKNOWN;
1785 else {
1786 flag++; /* prefixed with one dash, pass it */
1787 hit=-1;
1788 parse = flag;
1791 do {
1792 /* we can loop here if we have multiple single-letters */
1794 if(!longopt) {
1795 if(NULL != parse) {
1796 letter = (char)*parse;
1798 else {
1799 letter = '\0';
1801 subletter='\0';
1803 else {
1804 letter = parse[0];
1805 subletter = parse[1];
1807 *usedarg = FALSE; /* default is that we don't use the arg */
1809 if(hit < 0) {
1810 for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
1811 if(letter == aliases[j].letter[0]) {
1812 hit = j;
1813 break;
1816 if(hit < 0) {
1817 return PARAM_OPTION_UNKNOWN;
1820 if(hit < 0) {
1821 return PARAM_OPTION_UNKNOWN;
1823 if(aliases[hit].extraparam) {
1824 /* this option requires an extra parameter */
1825 if(!longopt && parse[1]) {
1826 nextarg=(char *)&parse[1]; /* this is the actual extra parameter */
1827 singleopt=TRUE; /* don't loop anymore after this */
1829 else if(!nextarg)
1830 return PARAM_REQUIRES_PARAMETER;
1831 else
1832 *usedarg = TRUE; /* mark it as used */
1835 switch(letter) {
1836 case '*': /* options without a short option */
1837 switch(subletter) {
1838 case 'a': /* random-file */
1839 GetStr(&config->random_file, nextarg);
1840 break;
1841 case 'b': /* egd-file */
1842 GetStr(&config->egd_file, nextarg);
1843 break;
1844 case 'c': /* connect-timeout */
1845 if(str2num(&config->connecttimeout, nextarg))
1846 return PARAM_BAD_NUMERIC;
1847 break;
1848 case 'd': /* ciphers */
1849 GetStr(&config->cipher_list, nextarg);
1850 break;
1851 case 'e': /* --disable-epsv */
1852 config->disable_epsv = toggle;
1853 break;
1854 case 'E': /* --epsv */
1855 config->disable_epsv = !toggle;
1856 break;
1857 #ifdef USE_ENVIRONMENT
1858 case 'f':
1859 config->writeenv = toggle;
1860 break;
1861 #endif
1862 case 'g': /* --trace */
1863 GetStr(&config->trace_dump, nextarg);
1864 if(config->tracetype && (config->tracetype != TRACE_BIN))
1865 warnf(config, "--trace overrides an earlier trace/verbose option\n");
1866 config->tracetype = TRACE_BIN;
1867 break;
1868 case 'h': /* --trace-ascii */
1869 GetStr(&config->trace_dump, nextarg);
1870 if(config->tracetype && (config->tracetype != TRACE_ASCII))
1871 warnf(config,
1872 "--trace-ascii overrides an earlier trace/verbose option\n");
1873 config->tracetype = TRACE_ASCII;
1874 break;
1875 case 'i': /* --limit-rate */
1877 /* We support G, M, K too */
1878 char *unit;
1879 curl_off_t value = curlx_strtoofft(nextarg, &unit, 0);
1881 if(!*unit)
1882 unit=(char *)"b";
1883 else if(strlen(unit) > 1)
1884 unit=(char *)"w"; /* unsupported */
1886 switch(*unit) {
1887 case 'G':
1888 case 'g':
1889 value *= 1024*1024*1024;
1890 break;
1891 case 'M':
1892 case 'm':
1893 value *= 1024*1024;
1894 break;
1895 case 'K':
1896 case 'k':
1897 value *= 1024;
1898 break;
1899 case 'b':
1900 case 'B':
1901 /* for plain bytes, leave as-is */
1902 break;
1903 default:
1904 warnf(config, "unsupported rate unit. Use G, M, K or B!\n");
1905 return PARAM_BAD_USE;
1907 config->recvpersecond = value;
1908 config->sendpersecond = value;
1910 break;
1912 case 'j': /* --compressed */
1913 config->encoding = toggle;
1914 break;
1916 case 'k': /* --digest */
1917 if(toggle)
1918 config->authtype |= CURLAUTH_DIGEST;
1919 else
1920 config->authtype &= ~CURLAUTH_DIGEST;
1921 break;
1923 case 'l': /* --negotiate */
1924 if(toggle) {
1925 if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE)
1926 config->authtype |= CURLAUTH_GSSNEGOTIATE;
1927 else
1928 return PARAM_LIBCURL_DOESNT_SUPPORT;
1930 else
1931 config->authtype &= ~CURLAUTH_GSSNEGOTIATE;
1932 break;
1934 case 'm': /* --ntlm */
1935 if(toggle) {
1936 if(curlinfo->features & CURL_VERSION_NTLM)
1937 config->authtype |= CURLAUTH_NTLM;
1938 else
1939 return PARAM_LIBCURL_DOESNT_SUPPORT;
1941 else
1942 config->authtype &= ~CURLAUTH_NTLM;
1943 break;
1945 case 'n': /* --basic for completeness */
1946 if(toggle)
1947 config->authtype |= CURLAUTH_BASIC;
1948 else
1949 config->authtype &= ~CURLAUTH_BASIC;
1950 break;
1952 case 'o': /* --anyauth, let libcurl pick it */
1953 if(toggle)
1954 config->authtype = CURLAUTH_ANY;
1955 /* --no-anyauth simply doesn't touch it */
1956 break;
1958 #ifdef MSDOS
1959 case 'p': /* --wdebug */
1960 dbug_init();
1961 break;
1962 #endif
1963 case 'q': /* --ftp-create-dirs */
1964 config->ftp_create_dirs = toggle;
1965 break;
1967 case 'r': /* --create-dirs */
1968 config->create_dirs = TRUE;
1969 break;
1971 case 's': /* --max-redirs */
1972 /* specified max no of redirects (http(s)) */
1973 if(str2num(&config->maxredirs, nextarg))
1974 return PARAM_BAD_NUMERIC;
1975 break;
1977 case 't': /* --proxy-ntlm */
1978 if(curlinfo->features & CURL_VERSION_NTLM)
1979 config->proxyntlm = toggle;
1980 else
1981 return PARAM_LIBCURL_DOESNT_SUPPORT;
1982 break;
1984 case 'u': /* --crlf */
1985 /* LF -> CRLF conversion? */
1986 config->crlf = TRUE;
1987 break;
1989 case 'v': /* --stderr */
1990 if(strcmp(nextarg, "-")) {
1991 FILE *newfile = fopen(nextarg, "wt");
1992 if(!newfile)
1993 warnf(config, "Failed to open %s!\n", nextarg);
1994 else {
1995 if(config->errors_fopened)
1996 fclose(config->errors);
1997 config->errors = newfile;
1998 config->errors_fopened = TRUE;
2001 else
2002 config->errors = stdout;
2003 break;
2004 case 'w': /* --interface */
2005 /* interface */
2006 GetStr(&config->iface, nextarg);
2007 break;
2008 case 'x': /* --krb */
2009 /* kerberos level string */
2010 if(curlinfo->features & (CURL_VERSION_KERBEROS4 |
2011 CURL_VERSION_GSSNEGOTIATE))
2012 GetStr(&config->krblevel, nextarg);
2013 else
2014 return PARAM_LIBCURL_DOESNT_SUPPORT;
2015 break;
2016 case 'y': /* --max-filesize */
2017 if(str2offset(&config->max_filesize, nextarg))
2018 return PARAM_BAD_NUMERIC;
2019 break;
2020 case 'z': /* --disable-eprt */
2021 config->disable_eprt = toggle;
2022 break;
2023 case 'Z': /* --eprt */
2024 config->disable_eprt = !toggle;
2025 break;
2027 default: /* the URL! */
2029 struct getout *url;
2030 if(config->url_get || (config->url_get=config->url_list)) {
2031 /* there's a node here, if it already is filled-in continue to find
2032 an "empty" node */
2033 while(config->url_get && (config->url_get->flags&GETOUT_URL))
2034 config->url_get = config->url_get->next;
2037 /* now there might or might not be an available node to fill in! */
2039 if(config->url_get)
2040 /* existing node */
2041 url = config->url_get;
2042 else
2043 /* there was no free node, create one! */
2044 url=new_getout(config);
2046 if(url) {
2047 /* fill in the URL */
2048 GetStr(&url->url, nextarg);
2049 url->flags |= GETOUT_URL;
2053 break;
2054 case '$': /* more options without a short option */
2055 switch(subletter) {
2056 case 'a': /* --ftp-ssl */
2057 config->ftp_ssl = toggle;
2058 break;
2059 case 'b': /* --ftp-pasv */
2060 if(config->ftpport)
2061 free(config->ftpport);
2062 config->ftpport = NULL;
2063 break;
2064 case 'c': /* --socks5 specifies a socks5 proxy to use, and resolves
2065 the name locally and passes on the resolved address */
2066 GetStr(&config->socksproxy, nextarg);
2067 config->socksver = CURLPROXY_SOCKS5;
2068 break;
2069 case 't': /* --socks4 specifies a socks4 proxy to use */
2070 GetStr(&config->socksproxy, nextarg);
2071 config->socksver = CURLPROXY_SOCKS4;
2072 break;
2073 case 'T': /* --socks4a specifies a socks4a proxy to use */
2074 GetStr(&config->socksproxy, nextarg);
2075 config->socksver = CURLPROXY_SOCKS4A;
2076 break;
2077 case '2': /* --socks5-hostname specifies a socks5 proxy and enables name
2078 resolving with the proxy */
2079 GetStr(&config->socksproxy, nextarg);
2080 config->socksver = CURLPROXY_SOCKS5_HOSTNAME;
2081 break;
2082 case 'd': /* --tcp-nodelay option */
2083 config->tcp_nodelay = toggle;
2084 break;
2085 case 'e': /* --proxy-digest */
2086 config->proxydigest = toggle;
2087 break;
2088 case 'f': /* --proxy-basic */
2089 config->proxybasic = toggle;
2090 break;
2091 case 'g': /* --retry */
2092 if(str2num(&config->req_retry, nextarg))
2093 return PARAM_BAD_NUMERIC;
2094 break;
2095 case 'h': /* --retry-delay */
2096 if(str2num(&config->retry_delay, nextarg))
2097 return PARAM_BAD_NUMERIC;
2098 break;
2099 case 'i': /* --retry-max-time */
2100 if(str2num(&config->retry_maxtime, nextarg))
2101 return PARAM_BAD_NUMERIC;
2102 break;
2104 case 'k': /* --proxy-negotiate */
2105 if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE)
2106 config->proxynegotiate = toggle;
2107 else
2108 return PARAM_LIBCURL_DOESNT_SUPPORT;
2109 break;
2110 case 'm': /* --ftp-account */
2111 GetStr(&config->ftp_account, nextarg);
2112 break;
2113 case 'n': /* --proxy-anyauth */
2114 config->proxyanyauth = toggle;
2115 break;
2116 case 'o': /* --trace-time */
2117 config->tracetime = toggle;
2118 break;
2119 case 'p': /* --ignore-content-length */
2120 config->ignorecl = toggle;
2121 break;
2122 case 'q': /* --ftp-skip-pasv-ip */
2123 config->ftp_skip_ip = toggle;
2124 break;
2125 case 'r': /* --ftp-method (undocumented at this point) */
2126 config->ftp_filemethod = ftpfilemethod(config, nextarg);
2127 break;
2128 case 's': /* --local-port */
2129 rc = sscanf(nextarg, "%d - %d",
2130 &config->localport,
2131 &config->localportrange);
2132 if(!rc)
2133 return PARAM_BAD_USE;
2134 else if(rc == 1)
2135 config->localportrange = 1; /* default number of ports to try */
2136 else {
2137 config->localportrange -= config->localport;
2138 if(config->localportrange < 1) {
2139 warnf(config, "bad range input\n");
2140 return PARAM_BAD_USE;
2143 break;
2144 case 'u': /* --ftp-alternative-to-user */
2145 GetStr(&config->ftp_alternative_to_user, nextarg);
2146 break;
2147 case 'v': /* --ftp-ssl-reqd */
2148 config->ftp_ssl_reqd = toggle;
2149 break;
2150 case 'w': /* --no-sessionid */
2151 config->disable_sessionid = !toggle;
2152 break;
2153 case 'x': /* --ftp-ssl-control */
2154 config->ftp_ssl_control = toggle;
2155 break;
2156 case 'y': /* --ftp-ssl-ccc */
2157 config->ftp_ssl_ccc = toggle;
2158 if(!config->ftp_ssl_ccc_mode)
2159 config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE;
2160 break;
2161 case 'j': /* --ftp-ssl-ccc-mode */
2162 config->ftp_ssl_ccc = TRUE;
2163 config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg);
2164 break;
2165 case 'z': /* --libcurl */
2166 GetStr(&config->libcurl, nextarg);
2167 break;
2168 case '#': /* --raw */
2169 config->raw = toggle;
2170 break;
2171 case '0': /* --post301 */
2172 config->post301 = toggle;
2173 break;
2174 case '1': /* --no-keepalive */
2175 config->nokeepalive = !toggle;
2176 break;
2177 case '3': /* --keepalive-time */
2178 if(str2num(&config->alivetime, nextarg))
2179 return PARAM_BAD_NUMERIC;
2180 break;
2182 break;
2183 case '#': /* --progress-bar */
2184 config->progressmode = toggle?CURL_PROGRESS_BAR:0;
2185 break;
2186 case '0':
2187 /* HTTP version 1.0 */
2188 config->httpversion = CURL_HTTP_VERSION_1_0;
2189 break;
2190 case '1':
2191 /* TLS version 1 */
2192 config->ssl_version = CURL_SSLVERSION_TLSv1;
2193 break;
2194 case '2':
2195 /* SSL version 2 */
2196 config->ssl_version = CURL_SSLVERSION_SSLv2;
2197 break;
2198 case '3':
2199 /* SSL version 3 */
2200 config->ssl_version = CURL_SSLVERSION_SSLv3;
2201 break;
2202 case '4':
2203 /* IPv4 */
2204 config->ip_version = 4;
2205 break;
2206 case '6':
2207 /* IPv6 */
2208 config->ip_version = 6;
2209 break;
2210 case 'a':
2211 /* This makes the FTP sessions use APPE instead of STOR */
2212 config->ftp_append = toggle;
2213 break;
2214 case 'A':
2215 /* This specifies the User-Agent name */
2216 GetStr(&config->useragent, nextarg);
2217 break;
2218 case 'b': /* cookie string coming up: */
2219 if(nextarg[0] == '@') {
2220 nextarg++;
2222 else if(strchr(nextarg, '=')) {
2223 /* A cookie string must have a =-letter */
2224 GetStr(&config->cookie, nextarg);
2225 break;
2227 /* We have a cookie file to read from! */
2228 GetStr(&config->cookiefile, nextarg);
2229 break;
2230 case 'B':
2231 /* use ASCII/text when transfering */
2232 config->use_ascii = toggle;
2233 break;
2234 case 'c':
2235 /* get the file name to dump all cookies in */
2236 GetStr(&config->cookiejar, nextarg);
2237 break;
2238 case 'C':
2239 /* This makes us continue an ftp transfer at given position */
2240 if(!curlx_strequal(nextarg, "-")) {
2241 if(str2offset(&config->resume_from, nextarg))
2242 return PARAM_BAD_NUMERIC;
2243 config->resume_from_current = FALSE;
2245 else {
2246 config->resume_from_current = TRUE;
2247 config->resume_from = 0;
2249 config->use_resume=TRUE;
2250 break;
2251 case 'd':
2252 /* postfield data */
2254 char *postdata=NULL;
2255 FILE *file;
2257 if(subletter == 'e') { /* --data-urlencode*/
2258 /* [name]=[content], we encode the content part only
2259 * [name]@[file name]
2261 * Case 2: we first load the file using that name and then encode
2262 * the content.
2264 const char *p = strchr(nextarg, '=');
2265 size_t size = 0;
2266 size_t nlen;
2267 char is_file;
2268 if(!p)
2269 /* there was no '=' letter, check for a '@' instead */
2270 p = strchr(nextarg, '@');
2271 if (p) {
2272 nlen = p - nextarg; /* length of the name part */
2273 is_file = *p++; /* pass the separator */
2275 else {
2276 /* neither @ nor =, so no name and it isn't a file */
2277 nlen = is_file = 0;
2278 p = nextarg;
2280 if('@' == is_file) {
2281 /* a '@' letter, it means that a file name or - (stdin) follows */
2283 if(curlx_strequal("-", p)) {
2284 file = stdin;
2285 SET_BINMODE(stdin);
2287 else {
2288 file = fopen(p, "rb");
2289 if(!file)
2290 warnf(config,
2291 "Couldn't read data from file \"%s\", this makes "
2292 "an empty POST.\n", nextarg);
2295 err = file2memory(&postdata, &size, file);
2297 if(file && (file != stdin))
2298 fclose(file);
2299 if(err)
2300 return err;
2302 else {
2303 GetStr(&postdata, p);
2304 size = strlen(postdata);
2307 if(!postdata) {
2308 /* no data from the file, point to a zero byte string to make this
2309 get sent as a POST anyway */
2310 postdata=strdup("");
2312 else {
2313 char *enc = curl_easy_escape(config->easy, postdata, size);
2314 if(enc) {
2315 /* now make a string with the name from above and append the
2316 encoded string */
2317 size_t outlen = nlen + strlen(enc) + 2;
2318 char *n = malloc(outlen);
2319 if(!n)
2320 return PARAM_NO_MEM;
2321 if (nlen > 0) /* only append '=' if we have a name */
2322 snprintf(n, outlen, "%.*s=%s", nlen, nextarg, enc);
2323 else
2324 strcpy(n, enc);
2325 curl_free(enc);
2326 free(postdata);
2327 if(n) {
2328 postdata = n;
2330 else
2331 return PARAM_NO_MEM;
2333 else
2334 return PARAM_NO_MEM;
2337 else if('@' == *nextarg) {
2338 size_t size = 0;
2339 /* the data begins with a '@' letter, it means that a file name
2340 or - (stdin) follows */
2341 nextarg++; /* pass the @ */
2343 if(curlx_strequal("-", nextarg)) {
2344 file = stdin;
2345 if(subletter == 'b') /* forced data-binary */
2346 SET_BINMODE(stdin);
2348 else {
2349 file = fopen(nextarg, "rb");
2350 if(!file)
2351 warnf(config, "Couldn't read data from file \"%s\", this makes "
2352 "an empty POST.\n", nextarg);
2355 if(subletter == 'b') {
2356 /* forced binary */
2357 err = file2memory(&postdata, &size, file);
2358 config->postfieldsize = (curl_off_t)size;
2360 else
2361 err = file2string(&postdata, file);
2363 if(file && (file != stdin))
2364 fclose(file);
2365 if(err)
2366 return err;
2368 if(!postdata) {
2369 /* no data from the file, point to a zero byte string to make this
2370 get sent as a POST anyway */
2371 postdata=strdup("");
2374 else {
2375 GetStr(&postdata, nextarg);
2378 #ifdef CURL_DOES_CONVERSIONS
2379 if(subletter != 'b') { /* NOT forced binary, convert to ASCII */
2380 convert_to_network(postdata, strlen(postdata));
2382 #endif
2384 if(config->postfields) {
2385 /* we already have a string, we append this one
2386 with a separating &-letter */
2387 char *oldpost=config->postfields;
2388 size_t newlen = strlen(oldpost) + strlen(postdata) + 2;
2389 config->postfields=malloc(newlen);
2390 if(!config->postfields) {
2391 free(postdata);
2392 return PARAM_NO_MEM;
2394 /* use ASCII value 0x26 for '&' to accommodate non-ASCII platforms */
2395 snprintf(config->postfields, newlen, "%s\x26%s", oldpost, postdata);
2396 free(oldpost);
2397 free(postdata);
2399 else
2400 config->postfields=postdata;
2403 We can't set the request type here, as this data might be used in
2404 a simple GET if -G is used. Already or soon.
2406 if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
2407 return PARAM_BAD_USE;
2409 break;
2410 case 'D':
2411 /* dump-header to given file name */
2412 GetStr(&config->headerfile, nextarg);
2413 break;
2414 case 'e':
2416 char *ptr = strstr(nextarg, ";auto");
2417 if(ptr) {
2418 /* Automatic referer requested, this may be combined with a
2419 set initial one */
2420 config->autoreferer = TRUE;
2421 *ptr = 0; /* zero terminate here */
2423 else
2424 config->autoreferer = FALSE;
2425 GetStr(&config->referer, nextarg);
2427 break;
2428 case 'E':
2429 switch(subletter) {
2430 case 'a': /* CA info PEM file */
2431 /* CA info PEM file */
2432 GetStr(&config->cacert, nextarg);
2433 break;
2434 case 'b': /* cert file type */
2435 GetStr(&config->cert_type, nextarg);
2436 break;
2437 case 'c': /* private key file */
2438 GetStr(&config->key, nextarg);
2439 break;
2440 case 'd': /* private key file type */
2441 GetStr(&config->key_type, nextarg);
2442 break;
2443 case 'e': /* private key passphrase */
2444 GetStr(&config->key_passwd, nextarg);
2445 cleanarg(nextarg);
2446 break;
2447 case 'f': /* crypto engine */
2448 GetStr(&config->engine, nextarg);
2449 if (config->engine && curlx_strequal(config->engine,"list"))
2450 config->list_engines = TRUE;
2451 break;
2452 case 'g': /* CA info PEM file */
2453 /* CA cert directory */
2454 GetStr(&config->capath, nextarg);
2455 break;
2456 case 'h': /* --pubkey public key file */
2457 GetStr(&config->pubkey, nextarg);
2458 break;
2459 case 'i': /* --hostpubmd5 md5 of the host public key */
2460 GetStr(&config->hostpubmd5, nextarg);
2461 if (!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
2462 return PARAM_BAD_USE;
2463 break;
2464 default: /* certificate file */
2466 char *ptr = strchr(nextarg, ':');
2467 /* Since we live in a world of weirdness and confusion, the win32
2468 dudes can use : when using drive letters and thus
2469 c:\file:password needs to work. In order not to break
2470 compatibility, we still use : as separator, but we try to detect
2471 when it is used for a file name! On windows. */
2472 #ifdef WIN32
2473 if(ptr &&
2474 (ptr == &nextarg[1]) &&
2475 (nextarg[2] == '\\' || nextarg[2] == '/') &&
2476 (ISALPHA(nextarg[0])) )
2477 /* colon in the second column, followed by a backslash, and the
2478 first character is an alphabetic letter:
2480 this is a drive letter colon */
2481 ptr = strchr(&nextarg[3], ':'); /* find the next one instead */
2482 #endif
2483 if(ptr) {
2484 /* we have a password too */
2485 *ptr=0;
2486 ptr++;
2487 GetStr(&config->key_passwd, ptr);
2489 GetStr(&config->cert, nextarg);
2490 cleanarg(nextarg);
2493 break;
2494 case 'f':
2495 /* fail hard on errors */
2496 config->failonerror = toggle;
2497 break;
2498 case 'F':
2499 /* "form data" simulation, this is a little advanced so lets do our best
2500 to sort this out slowly and carefully */
2501 if(formparse(config,
2502 nextarg,
2503 &config->httppost,
2504 &config->last_post,
2505 (bool) (subletter=='s'))) /* 's' means literal string */
2506 return PARAM_BAD_USE;
2507 if(SetHTTPrequest(config, HTTPREQ_POST, &config->httpreq))
2508 return PARAM_BAD_USE;
2509 break;
2511 case 'g': /* g disables URLglobbing */
2512 config->globoff = toggle;
2513 break;
2515 case 'G': /* HTTP GET */
2516 config->use_httpget = TRUE;
2517 break;
2519 case 'h': /* h for help */
2520 if(toggle) {
2521 help();
2522 return PARAM_HELP_REQUESTED;
2524 /* we now actually support --no-help too! */
2525 break;
2526 case 'H':
2527 /* A custom header to append to a list */
2528 err = add2list(&config->headers, nextarg);
2529 if(err)
2530 return err;
2531 break;
2532 case 'i':
2533 config->include_headers = toggle; /* include the headers as well in the
2534 general output stream */
2535 break;
2536 case 'j':
2537 config->cookiesession = toggle;
2538 break;
2539 case 'I':
2541 * no_body will imply include_headers later on
2543 config->no_body = toggle;
2544 if(SetHTTPrequest(config,
2545 (config->no_body)?HTTPREQ_HEAD:HTTPREQ_GET,
2546 &config->httpreq))
2547 return PARAM_BAD_USE;
2548 break;
2549 case 'k': /* allow insecure SSL connects */
2550 config->insecure_ok = toggle;
2551 break;
2552 case 'K': /* parse config file */
2553 if(parseconfig(nextarg, config))
2554 warnf(config, "error trying read config from the '%s' file\n",
2555 nextarg);
2556 break;
2557 case 'l':
2558 config->dirlistonly = toggle; /* only list the names of the FTP dir */
2559 break;
2560 case 'L':
2561 config->followlocation = toggle; /* Follow Location: HTTP headers */
2562 switch (subletter) {
2563 case 't':
2564 /* Continue to send authentication (user+password) when following
2565 * locations, even when hostname changed */
2566 config->unrestricted_auth = toggle;
2567 break;
2569 break;
2570 case 'm':
2571 /* specified max time */
2572 if(str2num(&config->timeout, nextarg))
2573 return PARAM_BAD_NUMERIC;
2574 break;
2575 case 'M': /* M for manual, huge help */
2576 if(toggle) { /* --no-manual shows no manual... */
2577 #ifdef USE_MANUAL
2578 hugehelp();
2579 return PARAM_HELP_REQUESTED;
2580 #else
2581 warnf(config,
2582 "built-in manual was disabled at build-time!\n");
2583 return PARAM_OPTION_UNKNOWN;
2584 #endif
2586 break;
2587 case 'n':
2588 switch(subletter) {
2589 case 'o': /* CA info PEM file */
2590 /* use .netrc or URL */
2591 config->netrc_opt = toggle;
2592 break;
2593 default:
2594 /* pick info from .netrc, if this is used for http, curl will
2595 automatically enfore user+password with the request */
2596 config->netrc = toggle;
2597 break;
2599 break;
2600 case 'N':
2601 /* disable the output I/O buffering */
2602 config->nobuffer = !toggle;
2603 break;
2604 case 'O': /* --remote-name */
2605 if(subletter == 'a') { /* --remote-name-all */
2606 config->default_node_flags = toggle?GETOUT_USEREMOTE:0;
2607 break;
2609 /* fall-through! */
2610 case 'o': /* --output */
2611 /* output file */
2613 struct getout *url;
2614 if(config->url_out || (config->url_out=config->url_list)) {
2615 /* there's a node here, if it already is filled-in continue to find
2616 an "empty" node */
2617 while(config->url_out && (config->url_out->flags&GETOUT_OUTFILE))
2618 config->url_out = config->url_out->next;
2621 /* now there might or might not be an available node to fill in! */
2623 if(config->url_out)
2624 /* existing node */
2625 url = config->url_out;
2626 else
2627 /* there was no free node, create one! */
2628 url=new_getout(config);
2630 if(url) {
2631 /* fill in the outfile */
2632 if('o' == letter) {
2633 GetStr(&url->outfile, nextarg);
2634 url->flags &= ~GETOUT_USEREMOTE; /* switch off */
2636 else {
2637 url->outfile=NULL; /* leave it */
2638 if(toggle)
2639 url->flags |= GETOUT_USEREMOTE; /* switch on */
2640 else
2641 url->flags &= ~GETOUT_USEREMOTE; /* switch off */
2643 url->flags |= GETOUT_OUTFILE;
2646 break;
2647 case 'P':
2648 /* This makes the FTP sessions use PORT instead of PASV */
2649 /* use <eth0> or <192.168.10.10> style addresses. Anything except
2650 this will make us try to get the "default" address.
2651 NOTE: this is a changed behaviour since the released 4.1!
2653 GetStr(&config->ftpport, nextarg);
2654 break;
2655 case 'p':
2656 /* proxy tunnel for non-http protocols */
2657 config->proxytunnel = toggle;
2658 break;
2660 case 'q': /* if used first, already taken care of, we do it like
2661 this so we don't cause an error! */
2662 break;
2663 case 'Q':
2664 /* QUOTE command to send to FTP server */
2665 err = PARAM_OK;
2666 switch(nextarg[0]) {
2667 case '-':
2668 /* prefixed with a dash makes it a POST TRANSFER one */
2669 nextarg++;
2670 err = add2list(&config->postquote, nextarg);
2671 break;
2672 case '+':
2673 /* prefixed with a plus makes it a just-before-transfer one */
2674 nextarg++;
2675 err = add2list(&config->prequote, nextarg);
2676 break;
2677 default:
2678 err = add2list(&config->quote, nextarg);
2679 break;
2681 if(err)
2682 return err;
2683 break;
2684 case 'r':
2685 /* Specifying a range WITHOUT A DASH will create an illegal HTTP range
2686 (and won't actually be range by definition). The man page previously
2687 claimed that to be a good way, why this code is added to work-around
2688 it. */
2689 if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
2690 char buffer[32];
2691 curl_off_t off;
2692 warnf(config,
2693 "A specified range MUST include at least one dash (-). "
2694 "Appending one for you!\n");
2695 off = curlx_strtoofft(nextarg, NULL, 10);
2696 snprintf(buffer, sizeof(buffer), "%Od-", off);
2697 GetStr(&config->range, buffer);
2700 /* byte range requested */
2701 char* tmp_range;
2702 tmp_range=nextarg;
2703 while(*tmp_range != '\0') {
2704 if(!ISDIGIT(*tmp_range)&&*tmp_range!='-'&&*tmp_range!=',') {
2705 warnf(config,"Invalid character is found in given range. "
2706 "A specified range MUST have only digits in "
2707 "\'start\'-\'stop\'. The server's response to this "
2708 "request is uncertain.\n");
2709 break;
2711 tmp_range++;
2713 /* byte range requested */
2714 GetStr(&config->range, nextarg);
2716 break;
2717 case 'R':
2718 /* use remote file's time */
2719 config->remote_time = toggle;
2720 break;
2721 case 's':
2722 /* don't show progress meter, don't show errors : */
2723 if(toggle)
2724 config->mute = config->noprogress = TRUE;
2725 else
2726 config->mute = config->noprogress = FALSE;
2727 config->showerror = !toggle; /* toggle off */
2728 break;
2729 case 'S':
2730 /* show errors */
2731 config->showerror = toggle; /* toggle on if used with -s */
2732 break;
2733 case 't':
2734 /* Telnet options */
2735 err = add2list(&config->telnet_options, nextarg);
2736 if(err)
2737 return err;
2738 break;
2739 case 'T':
2740 /* we are uploading */
2742 struct getout *url;
2743 if(config->url_out || (config->url_out=config->url_list)) {
2744 /* there's a node here, if it already is filled-in continue to find
2745 an "empty" node */
2746 while(config->url_out && (config->url_out->flags&GETOUT_UPLOAD))
2747 config->url_out = config->url_out->next;
2750 /* now there might or might not be an available node to fill in! */
2752 if(config->url_out)
2753 /* existing node */
2754 url = config->url_out;
2755 else
2756 /* there was no free node, create one! */
2757 url=new_getout(config);
2759 if(url) {
2760 url->flags |= GETOUT_UPLOAD; /* mark -T used */
2761 if(!*nextarg)
2762 url->flags |= GETOUT_NOUPLOAD;
2763 else {
2764 /* "-" equals stdin, but keep the string around for now */
2765 GetStr(&url->infile, nextarg);
2769 break;
2770 case 'u':
2771 /* user:password */
2772 GetStr(&config->userpwd, nextarg);
2773 cleanarg(nextarg);
2774 checkpasswd("host", &config->userpwd);
2775 break;
2776 case 'U':
2777 /* Proxy user:password */
2778 GetStr(&config->proxyuserpwd, nextarg);
2779 cleanarg(nextarg);
2780 checkpasswd("proxy", &config->proxyuserpwd);
2781 break;
2782 case 'v':
2783 if(toggle) {
2784 /* the '%' thing here will cause the trace get sent to stderr */
2785 GetStr(&config->trace_dump, (char *)"%");
2786 if(config->tracetype && (config->tracetype != TRACE_PLAIN))
2787 warnf(config,
2788 "-v/--verbose overrides an earlier trace/verbose option\n");
2789 config->tracetype = TRACE_PLAIN;
2791 else
2792 /* verbose is disabled here */
2793 config->tracetype = TRACE_NONE;
2794 break;
2795 case 'V':
2797 const char * const *proto;
2799 if(!toggle)
2800 /* --no-version yields no output! */
2801 break;
2803 printf(CURL_ID "%s\n", curl_version());
2804 if (curlinfo->protocols) {
2805 printf("Protocols: ");
2806 for (proto=curlinfo->protocols; *proto; ++proto) {
2807 printf("%s ", *proto);
2809 puts(""); /* newline */
2811 if(curlinfo->features) {
2812 unsigned int i;
2813 struct feat {
2814 const char *name;
2815 int bitmask;
2817 static const struct feat feats[] = {
2818 {"AsynchDNS", CURL_VERSION_ASYNCHDNS},
2819 {"Debug", CURL_VERSION_DEBUG},
2820 {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE},
2821 {"IDN", CURL_VERSION_IDN},
2822 {"IPv6", CURL_VERSION_IPV6},
2823 {"Largefile", CURL_VERSION_LARGEFILE},
2824 {"NTLM", CURL_VERSION_NTLM},
2825 {"SPNEGO", CURL_VERSION_SPNEGO},
2826 {"SSL", CURL_VERSION_SSL},
2827 {"SSPI", CURL_VERSION_SSPI},
2828 {"krb4", CURL_VERSION_KERBEROS4},
2829 {"libz", CURL_VERSION_LIBZ},
2830 {"CharConv", CURL_VERSION_CONV}
2832 printf("Features: ");
2833 for(i=0; i<sizeof(feats)/sizeof(feats[0]); i++) {
2834 if(curlinfo->features & feats[i].bitmask)
2835 printf("%s ", feats[i].name);
2837 puts(""); /* newline */
2840 return PARAM_HELP_REQUESTED;
2841 case 'w':
2842 /* get the output string */
2843 if('@' == *nextarg) {
2844 /* the data begins with a '@' letter, it means that a file name
2845 or - (stdin) follows */
2846 FILE *file;
2847 nextarg++; /* pass the @ */
2848 if(curlx_strequal("-", nextarg))
2849 file = stdin;
2850 else
2851 file = fopen(nextarg, "r");
2852 err = file2string(&config->writeout, file);
2853 if(file && (file != stdin))
2854 fclose(file);
2855 if(err)
2856 return err;
2857 if(!config->writeout)
2858 warnf(config, "Failed to read %s", file);
2860 else
2861 GetStr(&config->writeout, nextarg);
2862 break;
2863 case 'x':
2864 /* proxy */
2865 GetStr(&config->proxy, nextarg);
2866 break;
2867 case 'X':
2868 /* set custom request */
2869 GetStr(&config->customrequest, nextarg);
2870 break;
2871 case 'y':
2872 /* low speed time */
2873 if(str2num(&config->low_speed_time, nextarg))
2874 return PARAM_BAD_NUMERIC;
2875 if(!config->low_speed_limit)
2876 config->low_speed_limit = 1;
2877 break;
2878 case 'Y':
2879 /* low speed limit */
2880 if(str2num(&config->low_speed_limit, nextarg))
2881 return PARAM_BAD_NUMERIC;
2882 if(!config->low_speed_time)
2883 config->low_speed_time=30;
2884 break;
2885 case 'z': /* time condition coming up */
2886 switch(*nextarg) {
2887 case '+':
2888 nextarg++;
2889 default:
2890 /* If-Modified-Since: (section 14.28 in RFC2068) */
2891 config->timecond = CURL_TIMECOND_IFMODSINCE;
2892 break;
2893 case '-':
2894 /* If-Unmodified-Since: (section 14.24 in RFC2068) */
2895 config->timecond = CURL_TIMECOND_IFUNMODSINCE;
2896 nextarg++;
2897 break;
2898 case '=':
2899 /* Last-Modified: (section 14.29 in RFC2068) */
2900 config->timecond = CURL_TIMECOND_LASTMOD;
2901 nextarg++;
2902 break;
2904 now=time(NULL);
2905 config->condtime=curl_getdate(nextarg, &now);
2906 if(-1 == (int)config->condtime) {
2907 /* now let's see if it is a file name to get the time from instead! */
2908 struct_stat statbuf;
2909 if(-1 == stat(nextarg, &statbuf)) {
2910 /* failed, remove time condition */
2911 config->timecond = CURL_TIMECOND_NONE;
2912 warnf(config,
2913 "Illegal date format for -z/--timecond (and not "
2914 "a file name). Disabling time condition. "
2915 "See curl_getdate(3) for valid date syntax.\n");
2917 else {
2918 /* pull the time out from the file */
2919 config->condtime = statbuf.st_mtime;
2922 break;
2923 default: /* unknown flag */
2924 return PARAM_OPTION_UNKNOWN;
2926 hit = -1;
2928 } while(!longopt && !singleopt && *++parse && !*usedarg);
2930 return PARAM_OK;
2934 * Copies the string from line to the buffer at param, unquoting
2935 * backslash-quoted characters and NUL-terminating the output string.
2936 * Stops at the first non-backslash-quoted double quote character or the
2937 * end of the input string. param must be at least as long as the input
2938 * string. Returns the pointer after the last handled input character.
2940 static const char *unslashquote(const char *line, char *param)
2942 while(*line && (*line != '\"')) {
2943 if(*line == '\\') {
2944 char out;
2945 line++;
2947 /* default is to output the letter after the backslash */
2948 switch(out = *line) {
2949 case '\0':
2950 continue; /* this'll break out of the loop */
2951 case 't':
2952 out='\t';
2953 break;
2954 case 'n':
2955 out='\n';
2956 break;
2957 case 'r':
2958 out='\r';
2959 break;
2960 case 'v':
2961 out='\v';
2962 break;
2964 *param++=out;
2965 line++;
2967 else
2968 *param++=*line++;
2970 *param=0; /* always zero terminate */
2971 return line;
2974 /* return 0 on everything-is-fine, and non-zero otherwise */
2975 static int parseconfig(const char *filename,
2976 struct Configurable *config)
2978 int res;
2979 FILE *file;
2980 char filebuffer[512];
2981 bool usedarg;
2982 char *home;
2983 int rc = 0;
2985 if(!filename || !*filename) {
2986 /* NULL or no file name attempts to load .curlrc from the homedir! */
2988 #define CURLRC DOT_CHAR "curlrc"
2990 #ifndef __AMIGA__
2991 filename = CURLRC; /* sensible default */
2992 home = homedir(); /* portable homedir finder */
2993 if(home) {
2994 if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) {
2995 snprintf(filebuffer, sizeof(filebuffer),
2996 "%s%s%s", home, DIR_CHAR, CURLRC);
2998 #ifdef WIN32
2999 /* Check if the file exists - if not, try CURLRC in the same
3000 * directory as our executable
3002 file = fopen(filebuffer, "r");
3003 if (file != NULL) {
3004 fclose(file);
3005 filename = filebuffer;
3007 else {
3008 /* Get the filename of our executable. GetModuleFileName is
3009 * already declared via inclusions done in setup header file.
3010 * We assume that we are using the ASCII version here.
3012 int n = GetModuleFileName(0, filebuffer, sizeof(filebuffer));
3013 if (n > 0 && n < (int)sizeof(filebuffer)) {
3014 /* We got a valid filename - get the directory part */
3015 char *lastdirchar = strrchr(filebuffer, '\\');
3016 if (lastdirchar) {
3017 int remaining;
3018 *lastdirchar = 0;
3019 /* If we have enough space, build the RC filename */
3020 remaining = sizeof(filebuffer) - strlen(filebuffer);
3021 if ((int)strlen(CURLRC) < remaining - 1) {
3022 snprintf(lastdirchar, remaining,
3023 "%s%s", DIR_CHAR, CURLRC);
3024 /* Don't bother checking if it exists - we do
3025 * that later
3027 filename = filebuffer;
3032 #else /* WIN32 */
3033 filename = filebuffer;
3034 #endif /* WIN32 */
3036 free(home); /* we've used it, now free it */
3039 # else /* __AMIGA__ */
3040 /* On AmigaOS all the config files are into env:
3042 filename = "ENV:" CURLRC;
3044 #endif
3047 if(strcmp(filename,"-"))
3048 file = fopen(filename, "r");
3049 else
3050 file = stdin;
3052 if(file) {
3053 char *line;
3054 char *aline;
3055 char *option;
3056 char *param;
3057 int lineno=0;
3058 bool alloced_param;
3060 #define ISSEP(x) (((x)=='=') || ((x) == ':'))
3062 while (NULL != (aline = my_get_line(file))) {
3063 lineno++;
3064 line = aline;
3065 alloced_param=FALSE;
3067 /* line with # in the first non-blank column is a comment! */
3068 while(*line && ISSPACE(*line))
3069 line++;
3071 switch(*line) {
3072 case '#':
3073 case '/':
3074 case '\r':
3075 case '\n':
3076 case '*':
3077 case '\0':
3078 free(aline);
3079 continue;
3082 /* the option keywords starts here */
3083 option = line;
3084 while(*line && !ISSPACE(*line) && !ISSEP(*line))
3085 line++;
3086 /* ... and has ended here */
3088 if(*line)
3089 *line++=0; /* zero terminate, we have a local copy of the data */
3091 #ifdef DEBUG_CONFIG
3092 fprintf(stderr, "GOT: %s\n", option);
3093 #endif
3095 /* pass spaces and separator(s) */
3096 while(*line && (ISSPACE(*line) || ISSEP(*line)))
3097 line++;
3099 /* the parameter starts here (unless quoted) */
3100 if(*line == '\"') {
3101 /* quoted parameter, do the quote dance */
3102 line++;
3103 param=malloc(strlen(line)+1); /* parameter */
3104 if (!param) {
3105 /* out of memory */
3106 free(aline);
3107 rc = 1;
3108 break;
3110 alloced_param=TRUE;
3111 line = (char*) unslashquote(line, param);
3113 else {
3114 param=line; /* parameter starts here */
3115 while(*line && !ISSPACE(*line))
3116 line++;
3117 *line=0; /* zero terminate */
3120 if (param && !*param) {
3121 /* do this so getparameter can check for required parameters.
3122 Otherwise it always thinks there's a parameter. */
3123 if (alloced_param)
3124 free(param);
3125 param = NULL;
3128 #ifdef DEBUG_CONFIG
3129 fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)"));
3130 #endif
3131 res = getparameter(option, param, &usedarg, config);
3133 if (param && *param && !usedarg)
3134 /* we passed in a parameter that wasn't used! */
3135 res = PARAM_GOT_EXTRA_PARAMETER;
3137 if(res != PARAM_OK) {
3138 /* the help request isn't really an error */
3139 if(!strcmp(filename, "-")) {
3140 filename=(char *)"<stdin>";
3142 if(PARAM_HELP_REQUESTED != res) {
3143 const char *reason = param2text(res);
3144 warnf(config, "%s:%d: warning: '%s' %s\n",
3145 filename, lineno, option, reason);
3149 if(alloced_param)
3151 free(param);
3152 param = NULL;
3155 free(aline);
3157 if(file != stdin)
3158 fclose(file);
3160 else
3161 rc = 1; /* couldn't open the file */
3162 return rc;
3165 static void go_sleep(long ms)
3167 #ifdef HAVE_POLL_FINE
3168 /* portable subsecond "sleep" */
3169 poll((void *)0, 0, (int)ms);
3170 #else
3171 /* systems without poll() need other solutions */
3173 #ifdef WIN32
3174 /* Windows offers a millisecond sleep */
3175 Sleep(ms);
3176 #elif defined(MSDOS)
3177 delay(ms);
3178 #else
3179 /* Other systems must use select() for this */
3180 struct timeval timeout;
3182 timeout.tv_sec = ms/1000;
3183 ms = ms%1000;
3184 timeout.tv_usec = ms * 1000;
3186 select(0, NULL, NULL, NULL, &timeout);
3187 #endif
3189 #endif
3192 static size_t my_fwrite(void *buffer, size_t sz, size_t nmemb, void *stream)
3194 size_t rc;
3195 struct OutStruct *out=(struct OutStruct *)stream;
3196 struct Configurable *config = out->config;
3198 if(!out->stream) {
3199 /* open file for writing */
3200 out->stream=fopen(out->filename, "wb");
3201 if(!out->stream) {
3202 warnf(config, "Failed to create the file %s\n", out->filename);
3204 * Once that libcurl has called back my_fwrite() the returned value
3205 * is checked against the amount that was intended to be written, if
3206 * it does not match then it fails with CURLE_WRITE_ERROR. So at this
3207 * point returning a value different from sz*nmemb indicates failure.
3209 rc = (0 == (sz * nmemb)) ? 1 : 0;
3210 return rc; /* failure */
3214 rc = fwrite(buffer, sz, nmemb, out->stream);
3216 if((sz * nmemb) == rc) {
3217 /* we added this amount of data to the output */
3218 out->bytes += (sz * nmemb);
3221 if(config->nobuffer)
3222 /* disable output buffering */
3223 fflush(out->stream);
3225 return rc;
3228 struct InStruct {
3229 int fd;
3230 struct Configurable *config;
3233 #define MAX_SEEK 2147483647
3236 * my_seek() is the CURLOPT_SEEKFUNCTION we use
3238 static int my_seek(void *stream, curl_off_t offset, int whence)
3240 struct InStruct *in=(struct InStruct *)stream;
3242 #if (CURL_SIZEOF_CURL_OFF_T > SIZEOF_OFF_T) && !defined(USE_WIN32_LARGE_FILES)
3243 /* The offset check following here is only interesting if curl_off_t is
3244 larger than off_t and we are not using the WIN32 large file support
3245 macros that provide the support to do 64bit seeks correctly */
3247 if(offset > MAX_SEEK) {
3248 /* Some precaution code to work around problems with different data sizes
3249 to allow seeking >32bit even if off_t is 32bit. Should be very rare and
3250 is really valid on weirdo-systems. */
3251 curl_off_t left = offset;
3253 if(whence != SEEK_SET)
3254 /* this code path doesn't support other types */
3255 return 1;
3257 if(-1 == lseek(in->fd, 0, SEEK_SET))
3258 /* couldn't rewind to beginning */
3259 return 1;
3261 while(left) {
3262 long step = (left>MAX_SEEK ? MAX_SEEK : (long)left);
3263 if(-1 == lseek(in->fd, step, SEEK_CUR))
3264 /* couldn't seek forwards the desired amount */
3265 return 1;
3266 left -= step;
3268 return 0;
3270 #endif
3271 if(-1 == lseek(in->fd, offset, whence))
3272 /* couldn't rewind, the reason is in errno but errno is just not
3273 portable enough and we don't actually care that much why we failed. */
3274 return 1;
3276 return 0;
3279 static size_t my_fread(void *buffer, size_t sz, size_t nmemb, void *userp)
3281 ssize_t rc;
3282 struct InStruct *in=(struct InStruct *)userp;
3284 rc = read(in->fd, buffer, sz*nmemb);
3285 if(rc < 0)
3286 /* since size_t is unsigned we can't return negative values fine */
3287 return 0;
3288 return (size_t)rc;
3291 struct ProgressData {
3292 int calls;
3293 curl_off_t prev;
3294 int width;
3295 FILE *out; /* where to write everything to */
3296 curl_off_t initial_size;
3299 static int myprogress (void *clientp,
3300 double dltotal,
3301 double dlnow,
3302 double ultotal,
3303 double ulnow)
3305 /* The original progress-bar source code was written for curl by Lars Aas,
3306 and this new edition inherits some of his concepts. */
3308 char line[256];
3309 char outline[256];
3310 char format[40];
3311 double frac;
3312 double percent;
3313 int barwidth;
3314 int num;
3315 int i;
3317 struct ProgressData *bar = (struct ProgressData *)clientp;
3318 curl_off_t total = (curl_off_t)dltotal + (curl_off_t)ultotal +
3319 bar->initial_size; /* expected transfer size */
3320 curl_off_t point = (curl_off_t)dlnow + (curl_off_t)ulnow +
3321 bar->initial_size; /* we've come this far */
3323 if(point > total)
3324 /* we have got more than the expected total! */
3325 total = point;
3327 bar->calls++; /* simply count invokes */
3329 if(total < 1) {
3330 curl_off_t prevblock = bar->prev / 1024;
3331 curl_off_t thisblock = point / 1024;
3332 while ( thisblock > prevblock ) {
3333 fprintf( bar->out, "#" );
3334 prevblock++;
3337 else {
3338 frac = (double)point / (double)total;
3339 percent = frac * 100.0f;
3340 barwidth = bar->width - 7;
3341 num = (int) (((double)barwidth) * frac);
3342 i = 0;
3343 for ( i = 0; i < num; i++ ) {
3344 line[i] = '#';
3346 line[i] = '\0';
3347 snprintf( format, sizeof(format), "%%-%ds %%5.1f%%%%", barwidth );
3348 snprintf( outline, sizeof(outline), format, line, percent );
3349 fprintf( bar->out, "\r%s", outline );
3351 fflush(bar->out);
3352 bar->prev = point;
3354 return 0;
3357 static
3358 void progressbarinit(struct ProgressData *bar,
3359 struct Configurable *config)
3361 #ifdef __EMX__
3362 /* 20000318 mgs */
3363 int scr_size [2];
3364 #endif
3365 char *colp;
3367 memset(bar, 0, sizeof(struct ProgressData));
3369 /* pass this through to progress function so
3370 * it can display progress towards total file
3371 * not just the part that's left. (21-may-03, dbyron) */
3372 if (config->use_resume)
3373 bar->initial_size = config->resume_from;
3375 /* TODO: get terminal width through ansi escapes or something similar.
3376 try to update width when xterm is resized... - 19990617 larsa */
3377 #ifndef __EMX__
3378 /* 20000318 mgs
3379 * OS/2 users most likely won't have this env var set, and besides that
3380 * we're using our own way to determine screen width */
3381 colp = curlx_getenv("COLUMNS");
3382 if (colp != NULL) {
3383 bar->width = atoi(colp);
3384 curl_free(colp);
3386 else
3387 bar->width = 79;
3388 #else
3389 /* 20000318 mgs
3390 * We use this emx library call to get the screen width, and subtract
3391 * one from what we got in order to avoid a problem with the cursor
3392 * advancing to the next line if we print a string that is as long as
3393 * the screen is wide. */
3395 _scrsize(scr_size);
3396 bar->width = scr_size[0] - 1;
3397 #endif
3399 bar->out = config->errors;
3403 static
3404 void dump(const char *timebuf, const char *text,
3405 FILE *stream, const unsigned char *ptr, size_t size,
3406 trace tracetype, curl_infotype infotype)
3408 size_t i;
3409 size_t c;
3411 unsigned int width=0x10;
3413 if(tracetype == TRACE_ASCII)
3414 /* without the hex output, we can fit more on screen */
3415 width = 0x40;
3417 fprintf(stream, "%s%s, %zd bytes (0x%zx)\n", timebuf, text, size, size);
3419 for(i=0; i<size; i+= width) {
3421 fprintf(stream, "%04zx: ", i);
3423 if(tracetype == TRACE_BIN) {
3424 /* hex not disabled, show it */
3425 for(c = 0; c < width; c++)
3426 if(i+c < size)
3427 fprintf(stream, "%02x ", ptr[i+c]);
3428 else
3429 fputs(" ", stream);
3432 for(c = 0; (c < width) && (i+c < size); c++) {
3433 /* check for 0D0A; if found, skip past and start a new line of output */
3434 if ((tracetype == TRACE_ASCII) &&
3435 (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
3436 i+=(c+2-width);
3437 break;
3439 #ifdef CURL_DOES_CONVERSIONS
3440 /* repeat the 0D0A check above but use the host encoding for CRLF */
3441 if ((tracetype == TRACE_ASCII) &&
3442 (i+c+1 < size) && ptr[i+c]=='\r' && ptr[i+c+1]=='\n') {
3443 i+=(c+2-width);
3444 break;
3446 /* convert to host encoding and print this character */
3447 fprintf(stream, "%c", convert_char(infotype, ptr[i+c]));
3448 #else
3449 (void)infotype;
3450 fprintf(stream, "%c",
3451 (ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:UNPRINTABLE_CHAR);
3452 #endif /* CURL_DOES_CONVERSIONS */
3453 /* check again for 0D0A, to avoid an extra \n if it's at width */
3454 if ((tracetype == TRACE_ASCII) &&
3455 (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
3456 i+=(c+3-width);
3457 break;
3460 fputc('\n', stream); /* newline */
3462 fflush(stream);
3465 static
3466 int my_trace(CURL *handle, curl_infotype type,
3467 unsigned char *data, size_t size,
3468 void *userp)
3470 struct Configurable *config = (struct Configurable *)userp;
3471 FILE *output=config->errors;
3472 const char *text;
3473 struct timeval tv;
3474 struct tm *now;
3475 char timebuf[20];
3476 time_t secs;
3478 (void)handle; /* prevent compiler warning */
3480 tv = cutil_tvnow();
3481 secs = tv.tv_sec;
3482 now = localtime(&secs); /* not multithread safe but we don't care */
3483 if(config->tracetime)
3484 snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06d ",
3485 now->tm_hour, now->tm_min, now->tm_sec, tv.tv_usec);
3486 else
3487 timebuf[0]=0;
3489 if(!config->trace_stream) {
3490 /* open for append */
3491 if(curlx_strequal("-", config->trace_dump))
3492 config->trace_stream = stdout;
3493 else if(curlx_strequal("%", config->trace_dump))
3494 /* Ok, this is somewhat hackish but we do it undocumented for now */
3495 config->trace_stream = config->errors; /* aka stderr */
3496 else {
3497 config->trace_stream = fopen(config->trace_dump, "w");
3498 config->trace_fopened = TRUE;
3502 if(config->trace_stream)
3503 output = config->trace_stream;
3505 if(config->tracetype == TRACE_PLAIN) {
3507 * This is the trace look that is similar to what libcurl makes on its
3508 * own.
3510 static const char * const s_infotype[] = {
3511 "*", "<", ">", "{", "}", "{", "}"
3513 size_t i;
3514 size_t st=0;
3515 static bool newl = FALSE;
3516 static bool traced_data = FALSE;
3518 switch(type) {
3519 case CURLINFO_HEADER_OUT:
3520 for(i=0; i<size-1; i++) {
3521 if(data[i] == '\n') { /* LF */
3522 if(!newl) {
3523 fprintf(config->trace_stream, "%s%s ",
3524 timebuf, s_infotype[type]);
3526 fwrite(data+st, i-st+1, 1, config->trace_stream);
3527 st = i+1;
3528 newl = FALSE;
3531 if(!newl)
3532 fprintf(config->trace_stream, "%s%s ", timebuf, s_infotype[type]);
3533 fwrite(data+st, i-st+1, 1, config->trace_stream);
3534 newl = (bool)(size && (data[size-1] != '\n'));
3535 traced_data = FALSE;
3536 break;
3537 case CURLINFO_TEXT:
3538 case CURLINFO_HEADER_IN:
3539 if(!newl)
3540 fprintf(config->trace_stream, "%s%s ", timebuf, s_infotype[type]);
3541 fwrite(data, size, 1, config->trace_stream);
3542 newl = (bool)(size && (data[size-1] != '\n'));
3543 traced_data = FALSE;
3544 break;
3545 case CURLINFO_DATA_OUT:
3546 case CURLINFO_DATA_IN:
3547 case CURLINFO_SSL_DATA_IN:
3548 case CURLINFO_SSL_DATA_OUT:
3549 if(!traced_data) {
3550 /* if the data is output to a tty and we're sending this debug trace
3551 to stderr or stdout, we don't display the alert about the data not
3552 being shown as the data _is_ shown then just not via this
3553 function */
3554 if(!config->isatty ||
3555 ((config->trace_stream != stderr) &&
3556 (config->trace_stream != stdout))) {
3557 if(!newl)
3558 fprintf(config->trace_stream, "%s%s ", timebuf, s_infotype[type]);
3559 fprintf(config->trace_stream, "[data not shown]\n");
3560 newl = FALSE;
3561 traced_data = TRUE;
3564 break;
3565 default: /* nada */
3566 newl = FALSE;
3567 traced_data = FALSE;
3568 break;
3571 return 0;
3574 #ifdef CURL_DOES_CONVERSIONS
3575 /* Special processing is needed for CURLINFO_HEADER_OUT blocks
3576 * if they contain both headers and data (separated by CRLFCRLF).
3577 * We dump the header text and then switch type to CURLINFO_DATA_OUT.
3579 if((type == CURLINFO_HEADER_OUT) && (size > 4)) {
3580 size_t i;
3581 for(i = 0; i < size - 4; i++) {
3582 if(memcmp(&data[i], "\r\n\r\n", 4) == 0) {
3583 /* dump everthing through the CRLFCRLF as a sent header */
3584 text = "=> Send header";
3585 dump(timebuf, text, output, data, i+4, config->tracetype, type);
3586 data += i + 3;
3587 size -= i + 4;
3588 type = CURLINFO_DATA_OUT;
3589 data += 1;
3590 break;
3594 #endif /* CURL_DOES_CONVERSIONS */
3596 switch (type) {
3597 case CURLINFO_TEXT:
3598 fprintf(output, "%s== Info: %s", timebuf, data);
3599 default: /* in case a new one is introduced to shock us */
3600 return 0;
3602 case CURLINFO_HEADER_OUT:
3603 text = "=> Send header";
3604 break;
3605 case CURLINFO_DATA_OUT:
3606 text = "=> Send data";
3607 break;
3608 case CURLINFO_HEADER_IN:
3609 text = "<= Recv header";
3610 break;
3611 case CURLINFO_DATA_IN:
3612 text = "<= Recv data";
3613 break;
3614 case CURLINFO_SSL_DATA_IN:
3615 text = "<= Recv SSL data";
3616 break;
3617 case CURLINFO_SSL_DATA_OUT:
3618 text = "=> Send SSL data";
3619 break;
3622 dump(timebuf, text, output, data, size, config->tracetype, type);
3623 return 0;
3626 static void free_config_fields(struct Configurable *config)
3628 if(config->random_file)
3629 free(config->random_file);
3630 if(config->egd_file)
3631 free(config->egd_file);
3632 if(config->trace_dump)
3633 free(config->trace_dump);
3634 if(config->cipher_list)
3635 free(config->cipher_list);
3636 if(config->userpwd)
3637 free(config->userpwd);
3638 if(config->postfields)
3639 free(config->postfields);
3640 if(config->proxy)
3641 free(config->proxy);
3642 if(config->proxyuserpwd)
3643 free(config->proxyuserpwd);
3644 if(config->cookie)
3645 free(config->cookie);
3646 if(config->cookiefile)
3647 free(config->cookiefile);
3648 if(config->krblevel)
3649 free(config->krblevel);
3650 if(config->headerfile)
3651 free(config->headerfile);
3652 if(config->ftpport)
3653 free(config->ftpport);
3654 if(config->range)
3655 free(config->range);
3656 if(config->customrequest)
3657 free(config->customrequest);
3658 if(config->writeout)
3659 free(config->writeout);
3660 if(config->httppost)
3661 curl_formfree(config->httppost);
3662 if (config->cert)
3663 free(config->cert);
3664 if(config->cacert)
3665 free(config->cacert);
3666 if (config->cert_type)
3667 free(config->cert_type);
3668 if(config->capath)
3669 free(config->capath);
3670 if(config->cookiejar)
3671 free(config->cookiejar);
3672 if(config->ftp_account)
3673 free(config->ftp_account);
3674 if(config->ftp_alternative_to_user)
3675 free(config->ftp_alternative_to_user);
3676 if(config->iface)
3677 free(config->iface);
3678 if(config->socksproxy)
3679 free(config->socksproxy);
3680 if(config->libcurl)
3681 free(config->libcurl);
3682 if (config->key_passwd)
3683 free(config->key_passwd);
3684 if (config->key)
3685 free(config->key);
3686 if (config->key_type)
3687 free(config->key_type);
3688 if (config->pubkey)
3689 free(config->pubkey);
3690 if (config->referer)
3691 free(config->referer);
3692 if (config->hostpubmd5)
3693 free(config->hostpubmd5);
3695 curl_slist_free_all(config->quote); /* checks for config->quote == NULL */
3696 curl_slist_free_all(config->prequote);
3697 curl_slist_free_all(config->postquote);
3698 curl_slist_free_all(config->headers);
3699 curl_slist_free_all(config->telnet_options);
3701 if(config->easy)
3702 curl_easy_cleanup(config->easy);
3705 #ifdef WIN32
3707 /* Function to find CACert bundle on a Win32 platform using SearchPath.
3708 * (SearchPath is already declared via inclusions done in setup header file)
3709 * (Use the ASCII version instead of the unicode one!)
3710 * The order of the directories it searches is:
3711 * 1. application's directory
3712 * 2. current working directory
3713 * 3. Windows System directory (e.g. C:\windows\system32)
3714 * 4. Windows Directory (e.g. C:\windows)
3715 * 5. all directories along %PATH%
3717 static void FindWin32CACert(struct Configurable *config,
3718 const char *bundle_file)
3720 /* only check for cert file if "we" support SSL */
3721 if(curlinfo->features & CURL_VERSION_SSL) {
3722 DWORD buflen;
3723 char *ptr = NULL;
3724 char *retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
3725 if (!retval)
3726 return;
3727 retval[0] = '\0';
3728 buflen = SearchPathA(NULL, bundle_file, NULL, MAX_PATH+2, retval, &ptr);
3729 if (buflen > 0) {
3730 GetStr(&config->cacert, retval);
3732 free(retval);
3736 #endif
3738 #define RETRY_SLEEP_DEFAULT 1000 /* ms */
3739 #define RETRY_SLEEP_MAX 600000 /* ms == 10 minutes */
3741 static bool
3742 output_expected(const char* url, const char* uploadfile)
3744 if(!uploadfile)
3745 return TRUE; /* download */
3746 if(checkprefix("http://", url) || checkprefix("https://", url))
3747 return TRUE; /* HTTP(S) upload */
3749 return FALSE; /* non-HTTP upload, probably no output should be expected */
3752 #define my_setopt(x,y,z) _my_setopt(x, config, #y, y, z)
3754 static struct curl_slist *easycode;
3756 CURLcode _my_setopt(CURL *curl, struct Configurable *config, const char *name,
3757 CURLoption tag, ...);
3759 CURLcode _my_setopt(CURL *curl, struct Configurable *config, const char *name,
3760 CURLoption tag, ...)
3762 va_list arg;
3763 CURLcode ret;
3764 char *bufp;
3765 char value[256];
3766 bool remark=FALSE;
3768 va_start(arg, tag);
3770 if(tag < CURLOPTTYPE_OBJECTPOINT) {
3771 long lval = va_arg(arg, long);
3772 snprintf(value, sizeof(value), "%ld", lval);
3773 ret = curl_easy_setopt(curl, tag, lval);
3776 else if(tag < CURLOPTTYPE_OFF_T) {
3777 void *pval = va_arg(arg, void *);
3778 unsigned char *ptr = (unsigned char *)pval;
3780 /* function pointers are never printable */
3781 if (tag >= CURLOPTTYPE_FUNCTIONPOINT) {
3782 if (pval) {
3783 snprintf(value, sizeof(value), "%p", pval);
3784 remark = TRUE;
3786 else
3787 strcpy(value, "NULL");
3789 /* attempt to figure out if it is a string (since the tag numerical doesn't
3790 offer this info) and then output it as a string if so */
3791 else if(pval && ISGRAPH(ptr[0]) && ISGRAPH(ptr[1]) && ISGRAPH(ptr[2]))
3792 snprintf(value, sizeof(value), "\"%s\"", (char *)ptr);
3793 else if(pval) {
3794 snprintf(value, sizeof(value), "%p", pval);
3795 remark = TRUE;
3797 else {
3798 strcpy(value, "NULL"); /* value fits more than 5 bytes */
3800 ret = curl_easy_setopt(curl, tag, pval);
3803 else {
3804 curl_off_t oval = va_arg(arg, curl_off_t);
3805 snprintf(value, sizeof(value), "(curl_off_t)%Od", oval);
3806 ret = curl_easy_setopt(curl, tag, oval);
3809 if(config->libcurl) {
3810 /* we only use this for real if --libcurl was used */
3812 bufp = curlx_maprintf("%scurl_easy_setopt(hnd, %s, %s);%s",
3813 remark?"/* ":"", name, value,
3814 remark?" [REMARK] */":"");
3816 if (!bufp || !curl_slist_append(easycode, bufp))
3817 ret = CURLE_OUT_OF_MEMORY;
3818 if (bufp)
3819 curl_free(bufp);
3821 va_end(arg);
3823 return ret;
3826 static const char * const srchead[]={
3827 "/********* Sample code generated by the curl command line tool **********",
3828 " * Lines with [REMARK] below might need to be modified to make this code ",
3829 " * usable. Add error code checking where appropriate.",
3830 " * Compile this with a suitable header include path. Then link with ",
3831 " * libcurl.",
3832 " * If you use any *_LARGE options, make sure your compiler figure",
3833 " * out the correct size for the curl_off_t variable.",
3834 " * Read the details for all curl_easy_setopt() options online on:",
3835 " * http://curlm.haxx.se/libcurl/c/curl_easy_setopt.html",
3836 " ************************************************************************/",
3837 "[m]",
3838 "#include <curl/curl.h>",
3840 "int main(int argc, char *argv[])",
3841 "{",
3842 " CURLcode ret;",
3843 NULL
3846 static void dumpeasycode(struct Configurable *config)
3848 struct curl_slist *ptr = easycode;
3849 char *o = config->libcurl;
3851 if(o) {
3852 FILE *out;
3853 bool fopened = FALSE;
3854 if(strcmp(o, "-")) {
3855 out = fopen(o, "wt");
3856 fopened = TRUE;
3858 else
3859 out= stdout;
3860 if(!out)
3861 warnf(config, "Failed to open %s to write libcurl code!\n", o);
3862 else {
3863 int i;
3864 const char *c;
3866 for(i=0; (c = srchead[i]); i++) {
3867 if(!memcmp((char *)c, "[m]", 3)) {
3868 #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS > 32)
3869 fprintf(out, "#define _FILE_OFFSET_BITS %d "
3870 "/* for curl_off_t magic */\n",
3871 _FILE_OFFSET_BITS);
3872 #endif
3874 else
3875 fprintf(out, "%s\n", c);
3878 while(ptr) {
3879 fprintf(out, " %s\n", ptr->data);
3880 ptr = ptr->next;
3882 fprintf(out,
3883 " return (int)ret;\n"
3884 "}\n"
3885 "/**** End of sample code ****/\n");
3886 if(fopened)
3887 fclose(out);
3890 curl_slist_free_all(easycode);
3894 static int
3895 operate(struct Configurable *config, int argc, argv_item_t argv[])
3897 char errorbuffer[CURL_ERROR_SIZE];
3898 char useragent[256]; /* buah, we don't want a larger default user agent */
3899 struct ProgressData progressbar;
3900 struct getout *urlnode;
3901 struct getout *nextnode;
3903 struct OutStruct outs;
3904 struct OutStruct heads;
3905 struct InStruct input;
3907 URLGlob *urls=NULL;
3908 URLGlob *inglob=NULL;
3909 int urlnum;
3910 int infilenum;
3911 char *uploadfile=NULL; /* a single file, never a glob */
3913 curl_off_t uploadfilesize; /* -1 means unknown */
3914 bool stillflags=TRUE;
3916 bool allocuseragent=FALSE;
3918 char *httpgetfields=NULL;
3920 CURL *curl;
3921 int res = 0;
3922 int i;
3923 long retry_sleep_default;
3924 long retry_sleep;
3926 char *env;
3928 memset(&heads, 0, sizeof(struct OutStruct));
3930 #ifdef CURLDEBUG
3931 /* this sends all memory debug messages to a logfile named memdump */
3932 env = curlx_getenv("CURL_MEMDEBUG");
3933 if(env) {
3934 /* use the value as file name */
3935 char *s = strdup(env);
3936 curl_free(env);
3937 curl_memdebug(s);
3938 free(s);
3939 /* this weird strdup() and stuff here is to make the curl_free() get
3940 called before the memdebug() as otherwise the memdebug tracing will
3941 with tracing a free() without an alloc! */
3943 env = curlx_getenv("CURL_MEMLIMIT");
3944 if(env) {
3945 curl_memlimit(atoi(env));
3946 curl_free(env);
3948 #endif
3951 * Get a curl handle to use for all forthcoming curl transfers. Cleanup
3952 * when all transfers are done.
3954 curl = curl_easy_init();
3955 if(!curl) {
3956 clean_getout(config);
3957 return CURLE_FAILED_INIT;
3959 config->easy = curl;
3961 memset(&outs,0,sizeof(outs));
3963 config->outs = &outs;
3965 /* we get libcurl info right away */
3966 curlinfo = curl_version_info(CURLVERSION_NOW);
3968 errorbuffer[0]=0; /* prevent junk from being output */
3970 /* setup proper locale from environment */
3971 #ifdef HAVE_SETLOCALE
3972 setlocale(LC_ALL, "");
3973 #endif
3975 /* inits */
3976 if (main_init() != CURLE_OK) {
3977 helpf(config->errors, "error initializing curl library\n");
3978 return CURLE_FAILED_INIT;
3980 config->postfieldsize = -1;
3981 config->showerror=TRUE;
3982 config->use_httpget=FALSE;
3983 config->create_dirs=FALSE;
3984 config->lastrecvtime = cutil_tvnow();
3985 config->lastsendtime = cutil_tvnow();
3986 config->maxredirs = DEFAULT_MAXREDIRS;
3988 if(argc>1 &&
3989 (!curlx_strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
3990 strchr(argv[1], 'q')) {
3992 * The first flag, that is not a verbose name, but a shortname
3993 * and it includes the 'q' flag!
3997 else {
3998 parseconfig(NULL, config); /* ignore possible failure */
4001 if ((argc < 2) && !config->url_list) {
4002 helpf(config->errors, NULL);
4003 return CURLE_FAILED_INIT;
4006 /* Parse options */
4007 for (i = 1; i < argc; i++) {
4008 if(stillflags &&
4009 ('-' == argv[i][0])) {
4010 char *nextarg;
4011 bool passarg;
4012 char *origopt=argv[i];
4014 char *flag = argv[i];
4016 if(curlx_strequal("--", argv[i]))
4017 /* this indicates the end of the flags and thus enables the
4018 following (URL) argument to start with -. */
4019 stillflags=FALSE;
4020 else {
4021 nextarg= (i < argc - 1)? argv[i+1]: NULL;
4023 res = getparameter(flag, nextarg, &passarg, config);
4024 if(res) {
4025 int retval = CURLE_OK;
4026 if(res != PARAM_HELP_REQUESTED) {
4027 const char *reason = param2text(res);
4028 helpf(config->errors, "option %s: %s\n", origopt, reason);
4029 retval = CURLE_FAILED_INIT;
4031 clean_getout(config);
4032 return retval;
4035 if(passarg) /* we're supposed to skip this */
4036 i++;
4039 else {
4040 bool used;
4041 /* just add the URL please */
4042 res = getparameter((char *)"--url", argv[i], &used, config);
4043 if(res)
4044 return res;
4048 retry_sleep_default = config->retry_delay?
4049 config->retry_delay*1000:RETRY_SLEEP_DEFAULT; /* ms */
4050 retry_sleep = retry_sleep_default;
4052 if((!config->url_list || !config->url_list->url) && !config->list_engines) {
4053 clean_getout(config);
4054 helpf(config->errors, "no URL specified!\n");
4055 return CURLE_FAILED_INIT;
4057 if(NULL == config->useragent) {
4058 /* set non-zero default values: */
4059 snprintf(useragent, sizeof(useragent),
4060 CURL_NAME "/" CURL_VERSION " (" OS ") " "%s", curl_version());
4061 config->useragent= useragent;
4063 else
4064 allocuseragent = TRUE;
4066 /* On WIN32 we can't set the path to curl-ca-bundle.crt
4067 * at compile time. So we look here for the file in two ways:
4068 * 1: look at the environment variable CURL_CA_BUNDLE for a path
4069 * 2: if #1 isn't found, use the windows API function SearchPath()
4070 * to find it along the app's path (includes app's dir and CWD)
4072 * We support the environment variable thing for non-Windows platforms
4073 * too. Just for the sake of it.
4075 if (!config->cacert &&
4076 !config->capath &&
4077 !config->insecure_ok) {
4078 env = curlx_getenv("CURL_CA_BUNDLE");
4079 if(env)
4080 GetStr(&config->cacert, env);
4081 else {
4082 env = curlx_getenv("SSL_CERT_DIR");
4083 if(env)
4084 GetStr(&config->capath, env);
4085 else {
4086 env = curlx_getenv("SSL_CERT_FILE");
4087 if(env)
4088 GetStr(&config->cacert, env);
4092 if(env)
4093 curl_free(env);
4094 #ifdef WIN32
4095 else
4096 FindWin32CACert(config, "curl-ca-bundle.crt");
4097 #endif
4100 if (config->postfields) {
4101 if (config->use_httpget) {
4102 /* Use the postfields data for a http get */
4103 httpgetfields = strdup(config->postfields);
4104 free(config->postfields);
4105 config->postfields = NULL;
4106 if(SetHTTPrequest(config,
4107 (config->no_body?HTTPREQ_HEAD:HTTPREQ_GET),
4108 &config->httpreq)) {
4109 free(httpgetfields);
4110 return PARAM_BAD_USE;
4113 else {
4114 if(SetHTTPrequest(config, HTTPREQ_SIMPLEPOST, &config->httpreq))
4115 return PARAM_BAD_USE;
4119 /* This is the first entry added to easycode and it initializes the slist */
4120 easycode = curl_slist_append(easycode, "CURL *hnd = curl_easy_init();");
4121 if(!easycode) {
4122 clean_getout(config);
4123 res = CURLE_OUT_OF_MEMORY;
4124 goto quit_curl;
4127 if (config->list_engines) {
4128 struct curl_slist *engines = NULL;
4130 curl_easy_getinfo(curl, CURLINFO_SSL_ENGINES, &engines);
4131 list_engines(engines);
4132 curl_slist_free_all(engines);
4133 res = CURLE_OK;
4134 goto quit_curl;
4137 /* After this point, we should call curl_easy_cleanup() if we decide to bail
4138 * out from this function! */
4140 urlnode = config->url_list;
4142 if(config->headerfile) {
4143 /* open file for output: */
4144 if(strcmp(config->headerfile,"-")) {
4145 heads.filename = config->headerfile;
4147 else
4148 heads.stream=stdout;
4149 heads.config = config;
4152 /* loop through the list of given URLs */
4153 while(urlnode) {
4154 int up; /* upload file counter within a single upload glob */
4155 char *dourl;
4156 char *url;
4157 char *infiles; /* might be a glob pattern */
4158 char *outfiles=NULL;
4160 /* get the full URL (it might be NULL) */
4161 dourl=urlnode->url;
4163 url = dourl;
4165 if(NULL == url) {
4166 /* This node had no URL, skip it and continue to the next */
4167 if(urlnode->outfile)
4168 free(urlnode->outfile);
4170 /* move on to the next URL */
4171 nextnode=urlnode->next;
4172 free(urlnode); /* free the node */
4173 urlnode = nextnode;
4174 continue; /* next please */
4177 /* default output stream is stdout */
4178 outs.stream = stdout;
4179 outs.config = config;
4180 outs.bytes = 0; /* nothing written yet */
4182 /* save outfile pattern before expansion */
4183 if (urlnode->outfile) {
4184 outfiles = strdup(urlnode->outfile);
4185 if (!outfiles) {
4186 clean_getout(config);
4187 break;
4191 infiles = urlnode->infile;
4193 if(!config->globoff && infiles) {
4194 /* Unless explicitly shut off */
4195 res = glob_url(&inglob, infiles, &infilenum,
4196 config->showerror?config->errors:NULL);
4197 if(res != CURLE_OK) {
4198 clean_getout(config);
4199 if(outfiles)
4200 free(outfiles);
4201 break;
4205 /* Here's the loop for uploading multiple files within the same
4206 single globbed string. If no upload, we enter the loop once anyway. */
4207 for(up = 0;
4208 (!up && !infiles) ||
4209 (uploadfile = inglob?
4210 glob_next_url(inglob):
4211 (!up?strdup(infiles):NULL));
4212 up++) {
4213 int separator = 0;
4214 long retry_numretries;
4215 uploadfilesize=-1;
4217 if(!config->globoff) {
4218 /* Unless explicitly shut off, we expand '{...}' and '[...]'
4219 expressions and return total number of URLs in pattern set */
4220 res = glob_url(&urls, dourl, &urlnum,
4221 config->showerror?config->errors:NULL);
4222 if(res != CURLE_OK) {
4223 break;
4226 else
4227 urlnum = 1; /* without globbing, this is a single URL */
4229 /* if multiple files extracted to stdout, insert separators! */
4230 separator= ((!outfiles || curlx_strequal(outfiles, "-")) && urlnum > 1);
4232 /* Here's looping around each globbed URL */
4233 for(i = 0;
4234 (url = urls?glob_next_url(urls):(i?NULL:strdup(url)));
4235 i++) {
4236 int infd = STDIN_FILENO;
4237 bool infdopen;
4238 char *outfile;
4239 struct timeval retrystart;
4240 outfile = outfiles?strdup(outfiles):NULL;
4242 if((urlnode->flags&GETOUT_USEREMOTE) ||
4243 (outfile && !curlx_strequal("-", outfile)) ) {
4246 * We have specified a file name to store the result in, or we have
4247 * decided we want to use the remote file name.
4250 if(!outfile) {
4251 /* Find and get the remote file name */
4252 char * pc =strstr(url, "://");
4253 if(pc)
4254 pc+=3;
4255 else
4256 pc=url;
4257 pc = strrchr(pc, '/');
4259 if(pc) {
4260 /* duplicate the string beyond the slash */
4261 pc++;
4262 outfile = *pc ? strdup(pc): NULL;
4264 if(!outfile || !*outfile) {
4265 helpf(config->errors, "Remote file name has no length!\n");
4266 res = CURLE_WRITE_ERROR;
4267 free(url);
4268 break;
4270 #if defined(MSDOS)
4272 /* This is for DOS, and then we do some major replacing of
4273 bad characters in the file name before using it */
4274 char file1 [PATH_MAX];
4276 strcpy(file1, msdosify(outfile));
4277 free (outfile);
4278 outfile = strdup (rename_if_dos_device_name(file1));
4280 #endif /* MSDOS */
4282 else if(urls) {
4283 /* fill '#1' ... '#9' terms from URL pattern */
4284 char *storefile = outfile;
4285 outfile = glob_match_url(storefile, urls);
4286 free(storefile);
4287 if(!outfile) {
4288 /* bad globbing */
4289 warnf(config, "bad output glob!\n");
4290 free(url);
4291 res = CURLE_FAILED_INIT;
4292 break;
4296 /* Create the directory hierarchy, if not pre-existant to a multiple
4297 file output call */
4299 if(config->create_dirs &&
4300 (-1 == create_dir_hierarchy(outfile, config->errors)))
4301 return CURLE_WRITE_ERROR;
4303 if(config->resume_from_current) {
4304 /* We're told to continue from where we are now. Get the
4305 size of the file as it is now and open it for append instead */
4307 struct_stat fileinfo;
4309 /* VMS -- Danger, the filesize is only valid for stream files */
4310 if(0 == stat(outfile, &fileinfo))
4311 /* set offset to current file size: */
4312 config->resume_from = fileinfo.st_size;
4313 else
4314 /* let offset be 0 */
4315 config->resume_from = 0;
4318 outs.filename = outfile;
4320 if(config->resume_from) {
4321 outs.init = config->resume_from;
4322 /* open file for output: */
4323 outs.stream=(FILE *) fopen(outfile, config->resume_from?"ab":"wb");
4324 if (!outs.stream) {
4325 helpf(config->errors, "Can't open '%s'!\n", outfile);
4326 return CURLE_WRITE_ERROR;
4329 else {
4330 outs.stream = NULL; /* open when needed */
4333 infdopen=FALSE;
4334 if(uploadfile && !curlx_strequal(uploadfile, "-")) {
4336 * We have specified a file to upload and it isn't "-".
4338 struct_stat fileinfo;
4340 /* If no file name part is given in the URL, we add this file name */
4341 char *ptr=strstr(url, "://");
4342 if(ptr)
4343 ptr+=3;
4344 else
4345 ptr=url;
4346 ptr = strrchr(ptr, '/');
4347 if(!ptr || !strlen(++ptr)) {
4348 /* The URL has no file name part, add the local file name. In order
4349 to be able to do so, we have to create a new URL in another
4350 buffer.*/
4352 /* We only want the part of the local path that is on the right
4353 side of the rightmost slash and backslash. */
4354 char *filep = strrchr(uploadfile, '/');
4355 char *file2 = strrchr(filep?filep:uploadfile, '\\');
4357 if(file2)
4358 filep = file2+1;
4359 else if(filep)
4360 filep++;
4361 else
4362 filep = uploadfile;
4364 /* URL encode the file name */
4365 filep = curl_easy_escape(curl, filep, 0 /* use strlen */);
4367 if(filep) {
4368 char *urlbuffer=(char *)malloc(strlen(url) + strlen(filep) + 3);
4369 if(!urlbuffer) {
4370 helpf(config->errors, "out of memory\n");
4371 return CURLE_OUT_OF_MEMORY;
4373 if(ptr)
4374 /* there is a trailing slash on the URL */
4375 sprintf(urlbuffer, "%s%s", url, filep);
4376 else
4377 /* thers is no trailing slash on the URL */
4378 sprintf(urlbuffer, "%s/%s", url, filep);
4380 curl_free(filep);
4382 free(url);
4383 url = urlbuffer; /* use our new URL instead! */
4386 /* VMS Note:
4388 * Reading binary from files can be a problem... Only FIXED, VAR
4389 * etc WITHOUT implied CC will work Others need a \n appended to a
4390 * line
4392 * - Stat gives a size but this is UNRELIABLE in VMS As a f.e. a
4393 * fixed file with implied CC needs to have a byte added for every
4394 * record processed, this can by derived from Filesize & recordsize
4395 * for VARiable record files the records need to be counted! for
4396 * every record add 1 for linefeed and subtract 2 for the record
4397 * header for VARIABLE header files only the bare record data needs
4398 * to be considered with one appended if implied CC
4401 infd= open(uploadfile, O_RDONLY | O_BINARY);
4402 if ((infd == -1) || stat(uploadfile, &fileinfo)) {
4403 helpf(config->errors, "Can't open '%s'!\n", uploadfile);
4404 if(infd != -1)
4405 close(infd);
4407 /* Free the list of remaining URLs and globbed upload files
4408 * to force curl to exit immediately
4410 if(urls) {
4411 glob_cleanup(urls);
4412 urls = NULL;
4414 if(inglob) {
4415 glob_cleanup(inglob);
4416 inglob = NULL;
4419 res = CURLE_READ_ERROR;
4420 goto quit_urls;
4422 infdopen=TRUE;
4423 uploadfilesize=fileinfo.st_size;
4426 else if(uploadfile && curlx_strequal(uploadfile, "-")) {
4427 SET_BINMODE(stdin);
4428 infd = STDIN_FILENO;
4431 if(uploadfile && config->resume_from_current)
4432 config->resume_from = -1; /* -1 will then force get-it-yourself */
4434 if(output_expected(url, uploadfile)
4435 && outs.stream && isatty(fileno(outs.stream)))
4436 /* we send the output to a tty, therefore we switch off the progress
4437 meter */
4438 config->noprogress = config->isatty = TRUE;
4440 if (urlnum > 1 && !(config->mute)) {
4441 fprintf(config->errors, "\n[%d/%d]: %s --> %s\n",
4442 i+1, urlnum, url, outfile ? outfile : "<stdout>");
4443 if (separator)
4444 printf("%s%s\n", CURLseparator, url);
4446 if (httpgetfields) {
4447 char *urlbuffer;
4448 /* Find out whether the url contains a file name */
4449 const char *pc =strstr(url, "://");
4450 char sep='?';
4451 if(pc)
4452 pc+=3;
4453 else
4454 pc=url;
4456 pc = strrchr(pc, '/'); /* check for a slash */
4458 if(pc) {
4459 /* there is a slash present in the URL */
4461 if(strchr(pc, '?'))
4462 /* Ouch, there's already a question mark in the URL string, we
4463 then append the data with an ampersand separator instead! */
4464 sep='&';
4467 * Then append ? followed by the get fields to the url.
4469 urlbuffer=(char *)malloc(strlen(url) + strlen(httpgetfields) + 3);
4470 if(!urlbuffer) {
4471 helpf(config->errors, "out of memory\n");
4472 return CURLE_OUT_OF_MEMORY;
4474 if (pc)
4475 sprintf(urlbuffer, "%s%c%s", url, sep, httpgetfields);
4476 else
4477 /* Append / before the ? to create a well-formed url
4478 if the url contains a hostname only
4480 sprintf(urlbuffer, "%s/?%s", url, httpgetfields);
4482 free(url); /* free previous URL */
4483 url = urlbuffer; /* use our new URL instead! */
4486 if(!config->errors)
4487 config->errors = stderr;
4489 if(!outfile && !config->use_ascii) {
4490 /* We get the output to stdout and we have not got the ASCII/text
4491 flag, then set stdout to be binary */
4492 SET_BINMODE(stdout);
4495 if(1 == config->tcp_nodelay)
4496 my_setopt(curl, CURLOPT_TCP_NODELAY, 1);
4498 /* where to store */
4499 my_setopt(curl, CURLOPT_WRITEDATA, (FILE *)&outs);
4500 /* what call to write */
4501 my_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
4503 /* for uploads */
4504 input.fd = infd;
4505 input.config = config;
4506 my_setopt(curl, CURLOPT_READDATA, &input);
4507 /* what call to read */
4508 my_setopt(curl, CURLOPT_READFUNCTION, my_fread);
4510 /* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what
4511 CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */
4512 my_setopt(curl, CURLOPT_SEEKDATA, &input);
4513 my_setopt(curl, CURLOPT_SEEKFUNCTION, my_seek);
4515 if(config->recvpersecond)
4516 /* tell libcurl to use a smaller sized buffer as it allows us to
4517 make better sleeps! 7.9.9 stuff! */
4518 my_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond);
4520 /* size of uploaded file: */
4521 my_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);
4522 my_setopt(curl, CURLOPT_URL, url); /* what to fetch */
4523 my_setopt(curl, CURLOPT_PROXY, config->proxy); /* proxy to use */
4524 my_setopt(curl, CURLOPT_NOPROGRESS, config->noprogress);
4525 if(config->no_body) {
4526 my_setopt(curl, CURLOPT_NOBODY, 1);
4527 my_setopt(curl, CURLOPT_HEADER, 1);
4529 else
4530 my_setopt(curl, CURLOPT_HEADER, config->include_headers);
4532 my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror);
4533 my_setopt(curl, CURLOPT_UPLOAD, uploadfile?TRUE:FALSE);
4534 my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly);
4535 my_setopt(curl, CURLOPT_APPEND, config->ftp_append);
4537 if (config->netrc_opt)
4538 my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
4539 else if (config->netrc)
4540 my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED);
4541 else
4542 my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED);
4544 my_setopt(curl, CURLOPT_FOLLOWLOCATION, config->followlocation);
4545 my_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, config->unrestricted_auth);
4546 my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii);
4547 my_setopt(curl, CURLOPT_USERPWD, config->userpwd);
4548 my_setopt(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd);
4549 my_setopt(curl, CURLOPT_RANGE, config->range);
4550 my_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
4551 my_setopt(curl, CURLOPT_TIMEOUT, config->timeout);
4553 switch(config->httpreq) {
4554 case HTTPREQ_SIMPLEPOST:
4555 my_setopt(curl, CURLOPT_POSTFIELDS, config->postfields);
4556 my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, config->postfieldsize);
4557 break;
4558 case HTTPREQ_POST:
4559 my_setopt(curl, CURLOPT_HTTPPOST, config->httppost);
4560 break;
4561 default:
4562 break;
4564 my_setopt(curl, CURLOPT_REFERER, config->referer);
4565 my_setopt(curl, CURLOPT_AUTOREFERER, config->autoreferer);
4566 my_setopt(curl, CURLOPT_USERAGENT, config->useragent);
4567 my_setopt(curl, CURLOPT_FTPPORT, config->ftpport);
4568 my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
4569 config->low_speed_limit);
4570 my_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time);
4571 my_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE,
4572 config->sendpersecond);
4573 my_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE,
4574 config->recvpersecond);
4575 my_setopt(curl, CURLOPT_RESUME_FROM_LARGE,
4576 config->use_resume?config->resume_from:0);
4577 my_setopt(curl, CURLOPT_COOKIE, config->cookie);
4578 my_setopt(curl, CURLOPT_HTTPHEADER, config->headers);
4579 my_setopt(curl, CURLOPT_SSLCERT, config->cert);
4580 my_setopt(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
4581 my_setopt(curl, CURLOPT_SSLKEY, config->key);
4582 my_setopt(curl, CURLOPT_SSLKEYTYPE, config->key_type);
4583 my_setopt(curl, CURLOPT_KEYPASSWD, config->key_passwd);
4585 /* SSH private key uses the same command-line option as SSL private
4586 key */
4587 my_setopt(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key);
4588 my_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey);
4590 /* SSH host key md5 checking allows us to fail if we are
4591 * not talking to who we think we should
4593 my_setopt(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, config->hostpubmd5);
4596 /* default to strict verifyhost */
4597 my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
4598 if(config->cacert || config->capath) {
4599 if (config->cacert)
4600 my_setopt(curl, CURLOPT_CAINFO, config->cacert);
4602 if (config->capath)
4603 my_setopt(curl, CURLOPT_CAPATH, config->capath);
4604 my_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE);
4606 if(config->insecure_ok) {
4607 /* new stuff needed for libcurl 7.10 */
4608 my_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
4609 my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
4612 if(config->no_body || config->remote_time) {
4613 /* no body or use remote time */
4614 my_setopt(curl, CURLOPT_FILETIME, TRUE);
4617 my_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs);
4618 my_setopt(curl, CURLOPT_CRLF, config->crlf);
4619 my_setopt(curl, CURLOPT_QUOTE, config->quote);
4620 my_setopt(curl, CURLOPT_POSTQUOTE, config->postquote);
4621 my_setopt(curl, CURLOPT_PREQUOTE, config->prequote);
4622 my_setopt(curl, CURLOPT_WRITEHEADER,
4623 config->headerfile?&heads:NULL);
4624 my_setopt(curl, CURLOPT_COOKIEFILE, config->cookiefile);
4625 /* cookie jar was added in 7.9 */
4626 if(config->cookiejar)
4627 my_setopt(curl, CURLOPT_COOKIEJAR, config->cookiejar);
4628 /* cookie session added in 7.9.7 */
4629 my_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession);
4631 my_setopt(curl, CURLOPT_SSLVERSION, config->ssl_version);
4632 my_setopt(curl, CURLOPT_TIMECONDITION, config->timecond);
4633 my_setopt(curl, CURLOPT_TIMEVALUE, config->condtime);
4634 my_setopt(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
4635 my_setopt(curl, CURLOPT_STDERR, config->errors);
4637 /* three new ones in libcurl 7.3: */
4638 my_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel);
4639 my_setopt(curl, CURLOPT_INTERFACE, config->iface);
4640 my_setopt(curl, CURLOPT_KRBLEVEL, config->krblevel);
4642 progressbarinit(&progressbar, config);
4643 if((config->progressmode == CURL_PROGRESS_BAR) &&
4644 !config->noprogress && !config->mute) {
4645 /* we want the alternative style, then we have to implement it
4646 ourselves! */
4647 my_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress);
4648 my_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
4651 /* new in libcurl 7.6.2: */
4652 my_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options);
4654 /* new in libcurl 7.7: */
4655 my_setopt(curl, CURLOPT_RANDOM_FILE, config->random_file);
4656 my_setopt(curl, CURLOPT_EGDSOCKET, config->egd_file);
4657 my_setopt(curl, CURLOPT_CONNECTTIMEOUT, config->connecttimeout);
4659 if(config->cipher_list)
4660 my_setopt(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list);
4662 if(config->httpversion)
4663 my_setopt(curl, CURLOPT_HTTP_VERSION, config->httpversion);
4665 /* new in libcurl 7.9.2: */
4666 if(config->disable_epsv)
4667 /* disable it */
4668 my_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE);
4670 /* new in libcurl 7.10.5 */
4671 if(config->disable_eprt)
4672 /* disable it */
4673 my_setopt(curl, CURLOPT_FTP_USE_EPRT, FALSE);
4675 /* new in libcurl 7.10.6 (default is Basic) */
4676 if(config->authtype)
4677 my_setopt(curl, CURLOPT_HTTPAUTH, config->authtype);
4679 if(config->tracetype != TRACE_NONE) {
4680 my_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);
4681 my_setopt(curl, CURLOPT_DEBUGDATA, config);
4682 my_setopt(curl, CURLOPT_VERBOSE, TRUE);
4685 res = CURLE_OK;
4687 /* new in curl ?? */
4688 if (config->engine) {
4689 res = my_setopt(curl, CURLOPT_SSLENGINE, config->engine);
4690 my_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1);
4693 if (res != CURLE_OK)
4694 goto show_error;
4696 /* new in curl 7.10 */
4697 my_setopt(curl, CURLOPT_ENCODING,
4698 (config->encoding) ? "" : NULL);
4700 /* new in curl 7.10.7 */
4701 my_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS,
4702 config->ftp_create_dirs);
4703 if(config->proxyanyauth)
4704 my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
4705 else if(config->proxynegotiate)
4706 my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_GSSNEGOTIATE);
4707 else if(config->proxyntlm)
4708 my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
4709 else if(config->proxydigest)
4710 my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
4711 else if(config->proxybasic)
4712 my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
4714 /* new in curl 7.10.8 */
4715 if(config->max_filesize)
4716 my_setopt(curl, CURLOPT_MAXFILESIZE_LARGE,
4717 config->max_filesize);
4719 if(4 == config->ip_version)
4720 my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
4721 else if(6 == config->ip_version)
4722 my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
4723 else
4724 my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER);
4726 /* new in curl 7.15.5 */
4727 if(config->ftp_ssl_reqd)
4728 my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
4730 /* new in curl 7.11.0 */
4731 else if(config->ftp_ssl)
4732 my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
4734 /* new in curl 7.16.0 */
4735 else if(config->ftp_ssl_control)
4736 my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_CONTROL);
4738 /* new in curl 7.16.1 */
4739 if(config->ftp_ssl_ccc)
4740 my_setopt(curl, CURLOPT_FTP_SSL_CCC, config->ftp_ssl_ccc_mode);
4742 /* new in curl 7.11.1, modified in 7.15.2 */
4743 if(config->socksproxy) {
4744 my_setopt(curl, CURLOPT_PROXY, config->socksproxy);
4745 my_setopt(curl, CURLOPT_PROXYTYPE, config->socksver);
4748 /* curl 7.13.0 */
4749 my_setopt(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account);
4751 my_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl);
4753 /* curl 7.14.2 */
4754 my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip);
4756 /* curl 7.15.1 */
4757 my_setopt(curl, CURLOPT_FTP_FILEMETHOD, config->ftp_filemethod);
4759 /* curl 7.15.2 */
4760 if(config->localport) {
4761 my_setopt(curl, CURLOPT_LOCALPORT, config->localport);
4762 my_setopt(curl, CURLOPT_LOCALPORTRANGE,
4763 config->localportrange);
4766 /* curl 7.15.5 */
4767 my_setopt(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER,
4768 config->ftp_alternative_to_user);
4770 /* curl 7.16.0 */
4771 my_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE,
4772 !config->disable_sessionid);
4774 /* curl 7.16.2 */
4775 if(config->raw) {
4776 my_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, FALSE);
4777 my_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, FALSE);
4780 /* curl 7.17.1 */
4781 my_setopt(curl, CURLOPT_POST301, config->post301);
4782 if (!config->nokeepalive) {
4783 my_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockoptcallback);
4784 my_setopt(curl, CURLOPT_SOCKOPTDATA, config);
4787 retry_numretries = config->req_retry;
4789 retrystart = cutil_tvnow();
4791 do {
4792 res = curl_easy_perform(curl);
4793 if (!curl_slist_append(easycode, "ret = curl_easy_perform(hnd);")) {
4794 res = CURLE_OUT_OF_MEMORY;
4795 break;
4798 /* if retry-max-time is non-zero, make sure we haven't exceeded the
4799 time */
4800 if(retry_numretries &&
4801 (!config->retry_maxtime ||
4802 (cutil_tvdiff(cutil_tvnow(), retrystart)<
4803 config->retry_maxtime*1000)) ) {
4804 enum {
4805 RETRY_NO,
4806 RETRY_TIMEOUT,
4807 RETRY_HTTP,
4808 RETRY_FTP,
4809 RETRY_LAST /* not used */
4810 } retry = RETRY_NO;
4811 long response;
4812 if(CURLE_OPERATION_TIMEDOUT == res)
4813 /* retry timeout always */
4814 retry = RETRY_TIMEOUT;
4815 else if(CURLE_OK == res) {
4816 /* Check for HTTP transient errors */
4817 char *this_url=NULL;
4818 curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &this_url);
4819 if(this_url &&
4820 curlx_strnequal(this_url, "http", 4)) {
4821 /* This was HTTP(S) */
4822 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
4824 switch(response) {
4825 case 500: /* Internal Server Error */
4826 case 502: /* Bad Gateway */
4827 case 503: /* Service Unavailable */
4828 case 504: /* Gateway Timeout */
4829 retry = RETRY_HTTP;
4831 * At this point, we have already written data to the output
4832 * file (or terminal). If we write to a file, we must rewind
4833 * or close/re-open the file so that the next attempt starts
4834 * over from the beginning.
4836 * TODO: similar action for the upload case. We might need
4837 * to start over reading from a previous point if we have
4838 * uploaded something when this was returned.
4840 break;
4843 } /* if CURLE_OK */
4844 else if(CURLE_LOGIN_DENIED == res) {
4845 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
4847 if(response/100 == 5)
4849 * This is typically when the FTP server only allows a certain
4850 * amount of users and we are not one of them. It mostly
4851 * returns 530 in this case, but all 5xx codes are transient.
4853 retry = RETRY_FTP;
4856 if(retry) {
4857 static const char * const m[]={NULL,
4858 "timeout",
4859 "HTTP error",
4860 "FTP error"
4862 warnf(config, "Transient problem: %s "
4863 "Will retry in %ld seconds. "
4864 "%ld retries left.\n",
4865 m[retry],
4866 retry_sleep/1000,
4867 retry_numretries);
4869 go_sleep(retry_sleep);
4870 retry_numretries--;
4871 if(!config->retry_delay) {
4872 retry_sleep *= 2;
4873 if(retry_sleep > RETRY_SLEEP_MAX)
4874 retry_sleep = RETRY_SLEEP_MAX;
4876 if(outs.bytes && outs.filename) {
4877 /* We have written data to a output file, we truncate file
4879 if(!config->mute)
4880 fprintf(config->errors, "Throwing away %Od bytes\n",
4881 outs.bytes);
4882 fflush(outs.stream);
4883 /* truncate file at the position where we started appending */
4884 #ifdef HAVE_FTRUNCATE
4885 ftruncate( fileno(outs.stream), outs.init);
4886 /* now seek to the end of the file, the position where we
4887 just truncated the file in a large file-safe way */
4888 fseek(outs.stream, 0, SEEK_END);
4889 #else
4890 /* ftruncate is not available, so just reposition the file
4891 to the location we would have truncated it. This won't
4892 work properly with large files on 32-bit systems, but
4893 most of those will have ftruncate. */
4894 fseek(outs.stream, (long)outs.init, SEEK_SET);
4895 #endif
4896 outs.bytes = 0; /* clear for next round */
4898 continue;
4900 } /* if retry_numretries */
4902 /* In all ordinary cases, just break out of loop here */
4903 retry_sleep = retry_sleep_default;
4904 break;
4906 } while(1);
4908 if((config->progressmode == CURL_PROGRESS_BAR) &&
4909 progressbar.calls) {
4910 /* if the custom progress bar has been displayed, we output a
4911 newline here */
4912 fputs("\n", progressbar.out);
4915 if(config->writeout) {
4916 ourWriteOut(curl, config->writeout);
4918 #ifdef USE_ENVIRONMENT
4919 if (config->writeenv)
4920 ourWriteEnv(curl);
4921 #endif
4923 show_error:
4925 #ifdef VMS
4926 if (!config->showerror) {
4927 vms_show = VMSSTS_HIDE;
4929 #else
4930 if((res!=CURLE_OK) && config->showerror) {
4931 fprintf(config->errors, "curl: (%d) %s\n", (int)res,
4932 errorbuffer[0]? errorbuffer:
4933 curl_easy_strerror((CURLcode)res));
4934 if(CURLE_SSL_CACERT == res) {
4935 #define CURL_CA_CERT_ERRORMSG1 \
4936 "More details here: http://curl.haxx.se/docs/sslcerts.html\n\n" \
4937 "curl performs SSL certificate verification by default, using a \"bundle\"\n" \
4938 " of Certificate Authority (CA) public keys (CA certs). The default\n" \
4939 " bundle is named curl-ca-bundle.crt; you can specify an alternate file\n" \
4940 " using the --cacert option.\n"
4942 #define CURL_CA_CERT_ERRORMSG2 \
4943 "If this HTTPS server uses a certificate signed by a CA represented in\n" \
4944 " the bundle, the certificate verification probably failed due to a\n" \
4945 " problem with the certificate (it might be expired, or the name might\n" \
4946 " not match the domain name in the URL).\n" \
4947 "If you'd like to turn off curl's verification of the certificate, use\n" \
4948 " the -k (or --insecure) option.\n"
4950 fprintf(config->errors, "%s%s",
4951 CURL_CA_CERT_ERRORMSG1,
4952 CURL_CA_CERT_ERRORMSG2 );
4955 #endif
4957 if (outfile && !curlx_strequal(outfile, "-") && outs.stream)
4958 fclose(outs.stream);
4960 #ifdef HAVE_UTIME
4961 /* Important that we set the time _after_ the file has been
4962 closed, as is done above here */
4963 if(config->remote_time && outs.filename) {
4964 /* ask libcurl if we got a time. Pretty please */
4965 long filetime;
4966 curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
4967 if(filetime >= 0) {
4968 struct utimbuf times;
4969 times.actime = (time_t)filetime;
4970 times.modtime = (time_t)filetime;
4971 utime(outs.filename, &times); /* set the time we got */
4974 #endif
4975 #ifdef __AMIGA__
4976 /* Set the url as comment for the file. (up to 80 chars are allowed)
4978 if( strlen(url) > 78 )
4979 url[79] = '\0';
4981 SetComment( outs.filename, url);
4982 #endif
4984 quit_urls:
4985 if(url)
4986 free(url);
4988 if(outfile)
4989 free(outfile);
4991 if(infdopen)
4992 close(infd);
4994 } /* loop to the next URL */
4996 if(urls) {
4997 /* cleanup memory used for URL globbing patterns */
4998 glob_cleanup(urls);
4999 urls = NULL;
5002 if(uploadfile)
5003 free(uploadfile);
5005 } /* loop to the next globbed upload file */
5007 if(inglob) {
5008 glob_cleanup(inglob);
5009 inglob = NULL;
5012 if(outfiles)
5013 free(outfiles);
5015 /* empty this urlnode struct */
5016 if(urlnode->url)
5017 free(urlnode->url);
5018 if(urlnode->outfile)
5019 free(urlnode->outfile);
5020 if(urlnode->infile)
5021 free(urlnode->infile);
5023 /* move on to the next URL */
5024 nextnode=urlnode->next;
5025 free(urlnode); /* free the node */
5026 urlnode = nextnode;
5028 } /* while-loop through all URLs */
5030 quit_curl:
5031 if (httpgetfields)
5032 free(httpgetfields);
5034 if (config->engine)
5035 free(config->engine);
5037 /* cleanup the curl handle! */
5038 curl_easy_cleanup(curl);
5039 config->easy = NULL; /* cleanup now */
5040 if (easycode)
5041 curl_slist_append(easycode, "curl_easy_cleanup(hnd);");
5043 if(heads.stream && (heads.stream != stdout))
5044 fclose(heads.stream);
5046 if(allocuseragent)
5047 free(config->useragent);
5049 if(config->trace_fopened && config->trace_stream)
5050 fclose(config->trace_stream);
5052 if(config->errors_fopened)
5053 fclose(config->errors);
5055 main_free(); /* cleanup */
5057 dumpeasycode(config);
5059 return res;
5062 /* Ensure that file descriptors 0, 1 and 2 (stdin, stdout, stderr) are
5063 open before starting to run. Otherwise, the first three network
5064 sockets opened by curl could be used for input sources, downloaded data
5065 or error logs as they will effectively be stdin, stdout and/or stderr.
5067 static void checkfds(void)
5069 #ifdef HAVE_PIPE
5070 int fd[2] = { STDIN_FILENO, STDIN_FILENO };
5071 while( fd[0] == STDIN_FILENO ||
5072 fd[0] == STDOUT_FILENO ||
5073 fd[0] == STDERR_FILENO ||
5074 fd[1] == STDIN_FILENO ||
5075 fd[1] == STDOUT_FILENO ||
5076 fd[1] == STDERR_FILENO )
5077 if (pipe(fd) < 0)
5078 return; /* Out of handles. This isn't really a big problem now, but
5079 will be when we try to create a socket later. */
5080 close(fd[0]);
5081 close(fd[1]);
5082 #endif
5087 int main(int argc, char *argv[])
5089 int res;
5090 struct Configurable config;
5092 memset(&config, 0, sizeof(struct Configurable));
5094 config.errors = stderr; /* default errors to stderr */
5096 checkfds();
5098 res = operate(&config, argc, argv);
5099 #ifdef __SYMBIAN32__
5100 if (config.showerror)
5101 pressanykey();
5102 #endif
5103 free_config_fields(&config);
5105 #ifdef __NOVELL_LIBC__
5106 pressanykey();
5107 #endif
5108 #ifdef VMS
5109 if (res > CURL_LAST) res = CURL_LAST; /* If CURL_LAST exceeded then */
5110 return (vms_cond[res]|vms_show); /* curlmsg.h is out of sync. */
5111 #else
5112 return res;
5113 #endif
5117 * Reads a line from the given file, ensuring is NUL terminated.
5118 * The pointer must be freed by the caller.
5119 * NULL is returned on an out of memory condition.
5121 static char *my_get_line(FILE *fp)
5123 char buf[4096];
5124 char *nl = NULL;
5125 char *retval = NULL;
5127 do {
5128 if (NULL == fgets(buf, sizeof(buf), fp))
5129 break;
5130 if (NULL == retval)
5131 retval = strdup(buf);
5132 else {
5133 if (NULL == (retval = realloc(retval,
5134 strlen(retval) + strlen(buf) + 1)))
5135 break;
5136 strcat(retval, buf);
5139 while (NULL == (nl = strchr(retval, '\n')));
5141 if (NULL != nl)
5142 *nl = '\0';
5144 return retval;
5147 static void show_dir_errno(FILE *errors, const char *name)
5149 switch (ERRNO) {
5150 #ifdef EACCES
5151 case EACCES:
5152 fprintf(errors,"You don't have permission to create %s.\n", name);
5153 break;
5154 #endif
5155 #ifdef ENAMETOOLONG
5156 case ENAMETOOLONG:
5157 fprintf(errors,"The directory name %s is too long.\n", name);
5158 break;
5159 #endif
5160 #ifdef EROFS
5161 case EROFS:
5162 fprintf(errors,"%s resides on a read-only file system.\n", name);
5163 break;
5164 #endif
5165 #ifdef ENOSPC
5166 case ENOSPC:
5167 fprintf(errors,"No space left on the file system that will "
5168 "contain the directory %s.\n", name);
5169 break;
5170 #endif
5171 #ifdef EDQUOT
5172 case EDQUOT:
5173 fprintf(errors,"Cannot create directory %s because you "
5174 "exceeded your quota.\n", name);
5175 break;
5176 #endif
5177 default :
5178 fprintf(errors,"Error creating directory %s.\n", name);
5179 break;
5183 /* Create the needed directory hierarchy recursively in order to save
5184 multi-GETs in file output, ie:
5185 curl "http://my.site/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt"
5186 should create all the dir* automagically
5188 static int create_dir_hierarchy(const char *outfile, FILE *errors)
5190 char *tempdir;
5191 char *tempdir2;
5192 char *outdup;
5193 char *dirbuildup;
5194 int result=0;
5196 outdup = strdup(outfile);
5197 if(!outdup)
5198 return -1;
5200 dirbuildup = malloc(sizeof(char) * strlen(outfile));
5201 if(!dirbuildup) {
5202 free(outdup);
5203 return -1;
5205 dirbuildup[0] = '\0';
5207 tempdir = strtok(outdup, DIR_CHAR);
5209 while (tempdir != NULL) {
5210 tempdir2 = strtok(NULL, DIR_CHAR);
5211 /* since strtok returns a token for the last word even
5212 if not ending with DIR_CHAR, we need to prune it */
5213 if (tempdir2 != NULL) {
5214 if (strlen(dirbuildup) > 0)
5215 sprintf(dirbuildup,"%s%s%s",dirbuildup, DIR_CHAR, tempdir);
5216 else {
5217 if (0 != strncmp(outdup, DIR_CHAR, 1))
5218 sprintf(dirbuildup,"%s",tempdir);
5219 else
5220 sprintf(dirbuildup,"%s%s", DIR_CHAR, tempdir);
5222 if (access(dirbuildup, F_OK) == -1) {
5223 result = mkdir(dirbuildup,(mode_t)0000750);
5224 if (-1 == result) {
5225 show_dir_errno(errors, dirbuildup);
5226 break; /* get out of loop */
5230 tempdir = tempdir2;
5232 free(dirbuildup);
5233 free(outdup);
5235 return result; /* 0 is fine, -1 is badness */
5238 #ifdef MSDOS
5240 #ifndef HAVE_BASENAME
5241 /* basename() returns a pointer to the last component of a pathname.
5242 * Ripped from lib/formdata.c.
5244 static char *basename(char *path)
5246 /* Ignore all the details above for now and make a quick and simple
5247 implementaion here */
5248 char *s1;
5249 char *s2;
5251 s1=strrchr(path, '/');
5252 s2=strrchr(path, '\\');
5254 if(s1 && s2) {
5255 path = (s1 > s2? s1 : s2)+1;
5257 else if(s1)
5258 path = s1 + 1;
5259 else if(s2)
5260 path = s2 + 1;
5262 return path;
5264 #endif /* HAVE_BASENAME */
5266 /* The following functions are taken with modification from the DJGPP
5267 * port of tar 1.12. They use algorithms originally from DJTAR. */
5269 static const char *
5270 msdosify (const char *file_name)
5272 static char dos_name[PATH_MAX*2];
5273 static const char illegal_chars_dos[] = ".+, ;=[]|<>\\\":?*";
5274 static const char *illegal_chars_w95 = &illegal_chars_dos[8];
5275 int idx, dot_idx;
5276 const char *s = file_name;
5277 char *d = dos_name;
5278 const char *illegal_aliens = illegal_chars_dos;
5279 size_t len = sizeof (illegal_chars_dos) - 1;
5280 int lfn = 0;
5282 #ifdef DJGPP
5283 /* Support for Windows 9X VFAT systems, when available (djgpp only). */
5284 if (_use_lfn (file_name))
5285 lfn = 1;
5286 #endif
5287 if (lfn) {
5288 illegal_aliens = illegal_chars_w95;
5289 len -= (illegal_chars_w95 - illegal_chars_dos);
5292 /* Get past the drive letter, if any. */
5293 if (s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
5294 *d++ = *s++;
5295 *d++ = *s++;
5298 for (idx = 0, dot_idx = -1; *s; s++, d++) {
5299 if (memchr (illegal_aliens, *s, len)) {
5300 /* Dots are special: DOS doesn't allow them as the leading character,
5301 and a file name cannot have more than a single dot. We leave the
5302 first non-leading dot alone, unless it comes too close to the
5303 beginning of the name: we want sh.lex.c to become sh_lex.c, not
5304 sh.lex-c. */
5305 if (*s == '.') {
5306 if (idx == 0 && (s[1] == '/' || (s[1] == '.' && s[2] == '/'))) {
5307 /* Copy "./" and "../" verbatim. */
5308 *d++ = *s++;
5309 if (*s == '.')
5310 *d++ = *s++;
5311 *d = *s;
5313 else if (idx == 0)
5314 *d = '_';
5315 else if (dot_idx >= 0) {
5316 if (dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */
5317 d[dot_idx - idx] = '_'; /* replace previous dot */
5318 *d = '.';
5320 else
5321 *d = '-';
5323 else
5324 *d = '.';
5326 if (*s == '.')
5327 dot_idx = idx;
5329 else if (*s == '+' && s[1] == '+') {
5330 if (idx - 2 == dot_idx) { /* .c++, .h++ etc. */
5331 *d++ = 'x';
5332 *d = 'x';
5334 else {
5335 /* libg++ etc. */
5336 memcpy (d, "plus", 4);
5337 d += 3;
5339 s++;
5340 idx++;
5342 else
5343 *d = '_';
5345 else
5346 *d = *s;
5347 if (*s == '/') {
5348 idx = 0;
5349 dot_idx = -1;
5351 else
5352 idx++;
5355 *d = '\0';
5356 return dos_name;
5359 static char *
5360 rename_if_dos_device_name (char *file_name)
5362 /* We could have a file whose name is a device on MS-DOS. Trying to
5363 * retrieve such a file would fail at best and wedge us at worst. We need
5364 * to rename such files. */
5365 char *base;
5366 struct stat st_buf;
5367 char fname[PATH_MAX];
5369 strncpy(fname, file_name, PATH_MAX-1);
5370 fname[PATH_MAX-2] = 0; /* Leave room for an extra _ */
5371 base = basename (fname);
5372 if (((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
5373 size_t blen = strlen (base);
5375 /* Prepend a '_'. */
5376 memmove (base + 1, base, blen + 1);
5377 base[0] = '_';
5378 strcpy (file_name, fname);
5380 return file_name;
5382 #endif /* MSDOS */