1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2004, 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.247 2004/03/17 12:46:48 bagder Exp $
22 ***************************************************************************/
24 /* This is now designed to have its own local setup.h */
31 #include <sys/types.h>
36 #include <curl/curl.h>
38 #define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
39 #include <curl/mprintf.h>
48 #ifdef USE_ENVIRONMENT
52 #define CURLseparator "--_curl_--"
54 #if defined(WIN32)&&!defined(__CYGWIN32__)
58 #ifdef __NOVELL_LIBC__
62 #ifdef TIME_WITH_SYS_TIME
63 /* We can include both fine */
67 #ifdef HAVE_SYS_TIME_H
68 # include <sys/time.h>
77 #ifdef HAVE_IO_H /* typical win32 habit */
92 #ifdef HAVE_SYS_UTIME_H
93 #include <sys/utime.h>
102 #ifdef HAVE_SYS_POLL_H
103 #include <sys/poll.h>
106 #include <strtoofft.h> /* header from the libcurl directory */
108 /* The last #include file should be: */
110 /* This is low-level hard-hacking memory leak tracking and similar. Using
111 the library level code from this client-side is ugly, but we do this
112 anyway for convenience. */
113 #include "memdebug.h"
116 #define DEFAULT_MAXREDIRS 50L
119 void *xmalloc(size_t);
120 char *msdosify(char *);
121 char *rename_if_dos_device_name(char *);
125 int events; /* in param: what to poll for */
126 int revents; /* out param: what events occured */
128 int poll (struct pollfd *, int, int);
129 #endif /* __DJGPP__ */
137 #define CURL_PROGRESS_STATS 0 /* default progress display */
138 #define CURL_PROGRESS_BAR 1
145 #define MIN(X,Y) (((X) < (Y)) ? (X) : (Y))
158 /* Just a set of bits */
159 #define CONF_DEFAULT 0
161 #define CONF_AUTO_REFERER (1<<4) /* the automatic referer-system please! */
162 #define CONF_VERBOSE (1<<5) /* talk a lot */
163 #define CONF_HEADER (1<<8) /* throw the header out too */
164 #define CONF_NOPROGRESS (1<<10) /* shut off the progress meter */
165 #define CONF_NOBODY (1<<11) /* use HEAD to get http document */
166 #define CONF_FAILONERROR (1<<12) /* no output on http error codes >= 300 */
167 #define CONF_FTPLISTONLY (1<<16) /* Use NLST when listing ftp dir */
168 #define CONF_FTPAPPEND (1<<20) /* Append instead of overwrite on upload! */
169 #define CONF_NETRC (1<<22) /* read user+password from .netrc */
170 #define CONF_FOLLOWLOCATION (1<<23) /* use Location: Luke! */
171 #define CONF_GETTEXT (1<<24) /* use ASCII/text for transfer */
172 #define CONF_HTTPPOST (1<<25) /* multipart/form-data HTTP POST */
173 #define CONF_MUTE (1<<28) /* force NOPROGRESS */
175 #define CONF_NETRC_OPT (1<<29) /* read user+password from either
177 #define CONF_UNRESTRICTED_AUTH (1<<30)
178 /* Send authentication (user+password) when following
179 * locations, even when hostname changed */
182 /* Ultrix doesn't have strdup(), so make a quick clone: */
183 char *strdup(char *str)
189 newstr = (char *) malloc((len+1)*sizeof(char));
203 #define mkdir(x,y) (mkdir)(x)
207 #include "curlmsg_vms.h"
211 * This is the main global constructor for the app. Call this before
212 * _any_ libcurl usage. If this fails, *NO* libcurl functions may be
213 * used, or havoc may be the result.
215 static CURLcode main_init(void)
217 return curl_global_init(CURL_GLOBAL_DEFAULT);
221 * This is the main global destructor for the app. Call this after
222 * _all_ libcurl usage is done.
224 static void main_free(void)
226 curl_global_cleanup();
229 static int SetHTTPrequest(HttpReq req, HttpReq *store)
231 if((*store == HTTPREQ_UNSPEC) ||
236 fprintf(stderr, "You can only select one HTTP request!\n");
240 static void helpf(const char *fmt, ...)
245 fputs("curl: ", stderr); /* prefix it */
246 vfprintf(stderr, fmt, ap);
249 fprintf(stderr, "curl: try 'curl --help' "
251 "or 'curl --manual' "
253 "for more information\n");
257 * A chain of these nodes contain URL to get and where to put the URL's
261 struct getout *next; /* next one */
262 char *url; /* the URL we deal with */
263 char *outfile; /* where to store the output */
264 char *infile; /* file to upload, if GETOUT_UPLOAD is set */
265 int flags; /* options */
267 #define GETOUT_OUTFILE (1<<0) /* set when outfile is deemed done */
268 #define GETOUT_URL (1<<1) /* set when URL is deemed done */
269 #define GETOUT_USEREMOTE (1<<2) /* use remote file name locally */
270 #define GETOUT_UPLOAD (1<<3) /* if set, -T has been used */
271 #define GETOUT_NOUPLOAD (1<<4) /* if set, -T "" has been used */
274 static void help(void)
277 static const char *helptext[]={
278 "Usage: curl [options...] <url>",
279 "Options: (H) means HTTP/HTTPS only, (F) means FTP only",
280 " -a/--append Append to target file when uploading (F)",
281 " -A/--user-agent <string> User-Agent to send to server (H)",
282 " --anyauth Tell curl to choose authentication method (H)",
283 " -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)",
284 " --basic Enable HTTP Basic Authentication (H)",
285 " -B/--use-ascii Use ASCII/text transfer",
286 " -c/--cookie-jar <file> Write cookies to this file after operation (H)",
287 " -C/--continue-at <offset> Resumed transfer offset",
288 " -d/--data <data> HTTP POST data (H)",
289 " --data-ascii <data> HTTP POST ASCII data (H)",
290 " --data-binary <data> HTTP POST binary data (H)",
291 " --negotiate Enable HTTP Negotiate Authentication (H)",
292 " --digest Enable HTTP Digest Authentication (H)",
293 " --disable-eprt Prevent curl from using EPRT or LPRT (F)",
294 " --disable-epsv Prevent curl from using EPSV (F)",
295 " -D/--dump-header <file> Write the headers to this file",
296 " --egd-file <file> EGD socket path for random data (SSL)",
297 #ifdef USE_ENVIRONMENT
298 " --environment Write result codes to environment variables (RISC OS)",
300 " -e/--referer Referer URL (H)",
301 " -E/--cert <cert[:passwd]> Client certificate file and password (SSL)",
302 " --cert-type <type> Certificate file type (DER/PEM/ENG) (SSL)",
303 " --key <key> Private key file name (SSL)",
304 " --key-type <type> Private key file type (DER/PEM/ENG) (SSL)",
305 " --pass <pass> Pass phrase for the private key (SSL)",
306 " --engine <eng> Crypto engine to use (SSL)",
307 " --cacert <file> CA certificate to verify peer against (SSL)",
308 " --capath <directory> CA directory (made using c_rehash) to verify",
309 " peer against (SSL)",
310 " --ciphers <list> SSL ciphers to use (SSL)",
311 " --compressed Request compressed response (using deflate or gzip)",
312 " --connect-timeout <seconds> Maximum time allowed for connection",
313 " --create-dirs Create necessary local directory hierarchy",
314 " --crlf Convert LF to CRLF in upload",
315 " -f/--fail Fail silently (no output at all) on errors (H)",
316 " --ftp-create-dirs Create the remote dirs if not present (F)",
317 " --ftp-pasv Use PASV instead of PORT (F)",
318 " --ftp-ssl Enable SSL/TLS for the ftp transfer (F)",
319 " -F/--form <name=content> Specify HTTP multipart POST data (H)",
320 " -g/--globoff Disable URL sequences and ranges using {} and []",
321 " -G/--get Send the -d data with a HTTP GET (H)",
322 " -h/--help This help text",
323 " -H/--header <line> Custom header to pass to server (H)",
324 " -i/--include Include protocol headers in the output (H/F)",
325 " -I/--head Show document info only",
326 " -j/--junk-session-cookies Ignore session cookies read from file (H)",
327 " --interface <interface> Specify network interface to use",
328 " --krb4 <level> Enable krb4 with specified security level (F)",
329 " -k/--insecure Allow curl to connect to SSL sites without certs (H)",
330 " -K/--config Specify which config file to read",
331 " -l/--list-only List only names of an FTP directory (F)",
332 " --limit-rate <rate> Limit transfer speed to this rate",
333 " -L/--location Follow Location: hints (H)",
334 " --location-trusted Follow Location: and send authentication even ",
335 " to other hostnames (H)",
336 " -m/--max-time <seconds> Maximum time allowed for the transfer",
337 " --max-redirs <num> Maximum number of redirects allowed (H)",
338 " --max-filesize <bytes> Maximum file size to download (H/F)",
339 " -M/--manual Display the full manual",
340 " -n/--netrc Must read .netrc for user name and password",
341 " --netrc-optional Use either .netrc or URL; overrides -n",
342 " --ntlm Enable HTTP NTLM authentication (H)",
343 " -N/--no-buffer Disable buffering of the output stream",
344 " -o/--output <file> Write output to <file> instead of stdout",
345 " -O/--remote-name Write output to a file named as the remote file",
346 " -p/--proxytunnel Operate through a HTTP proxy tunnel (using CONNECT)",
347 " --proxy-ntlm Enable NTLM authentication on the proxy (H)",
348 " -P/--ftp-port <address> Use PORT with address instead of PASV (F)",
349 " -q If used as the first parameter disables .curlrc",
350 " -Q/--quote <cmd> Send command(s) to server before file transfer (F)",
351 " -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server",
352 " --random-file <file> File for reading random data from (SSL)",
353 " -R/--remote-time Set the remote file's time on the local output",
354 " -s/--silent Silent mode. Don't output anything",
355 " -S/--show-error Show error. With -s, make curl show errors when they occur",
356 " --socks <host[:port]> Use SOCKS5 proxy on given host + port",
357 " --stderr <file> Where to redirect stderr. - means stdout",
358 " -t/--telnet-option <OPT=val> Set telnet option",
359 " --trace <file> Dump a network/debug trace to the given file",
360 " --trace-ascii <file> Like --trace but without the hex output",
361 " -T/--upload-file <file> Transfer/upload <file> to remote site",
362 " --url <URL> Another way to specify URL to work with",
363 " -u/--user <user[:password]> Specify user and password to use",
364 " Overrides -n and --netrc-optional",
365 " -U/--proxy-user <user[:password]> Specify Proxy authentication",
366 " -v/--verbose Make the operation more talkative",
367 " -V/--version Show version number and quit",
369 " --wdebug Turn on WATT-32 debugging under DJGPP",
371 " -w/--write-out [format] What to output after completion",
372 " -x/--proxy <host[:port]> Use HTTP proxy on given port",
373 " -X/--request <command> Specify request command to use",
374 " -y/--speed-time Time needed to trig speed-limit abort. Defaults to 30",
375 " -Y/--speed-limit Stop transfer if below speed-limit for 'speed-time' secs",
376 " -z/--time-cond <time> Transfer based on a time condition",
377 " -0/--http1.0 Use HTTP 1.0 (H)",
378 " -1/--tlsv1 Use TLSv1 (SSL)",
379 " -2/--sslv2 Use SSLv2 (SSL)",
380 " -3/--sslv3 Use SSLv3 (SSL)",
381 " -4/--ipv4 Resolve name to IPv4 address",
382 " -6/--ipv6 Resolve name to IPv6 address",
383 " -#/--progress-bar Display transfer progress as a progress bar",
386 for(i=0; helptext[i]; i++) {
388 #ifdef __NOVELL_LIBC__
389 if (i && ((i % 23) == 0))
401 struct Configurable {
406 char *cookie; /* single line with specified cookies */
407 char *cookiejar; /* write to this file */
408 char *cookiefile; /* read from this file */
409 bool cookiesession; /* new session? */
410 bool encoding; /* Accept-Encoding please */
411 long authtype; /* auth bitmask */
413 bool resume_from_current;
416 curl_off_t resume_from;
423 curl_off_t max_filesize;
427 unsigned short porttouse;
429 long low_speed_limit;
438 struct getout *url_list; /* point to the first node */
439 struct getout *url_last; /* point to the last/current node */
441 struct getout *url_get; /* point to the node to fill in URL */
442 struct getout *url_out; /* point to the node to fill in outfile */
456 char *trace_dump; /* file to dump the network trace to, or NULL */
466 bool insecure_ok; /* set TRUE to allow insecure SSL connects */
468 bool ftp_create_dirs;
471 char *writeout; /* %-styled format string to output */
472 bool writeenv; /* write results to environment, if available */
474 FILE *errors; /* if stderr redirect is requested */
477 struct curl_slist *quote;
478 struct curl_slist *postquote;
479 struct curl_slist *prequote;
483 curl_TimeCond timecond;
486 struct curl_slist *headers;
488 struct curl_httppost *httppost;
489 struct curl_httppost *last_post;
491 struct curl_slist *telnet_options;
495 /* for bandwidth limiting features: */
497 size_t sendpersecond; /* send to peer */
498 size_t recvpersecond; /* receive from peer */
511 /* global variable to hold info about libcurl */
512 static curl_version_info_data *curlinfo;
514 static void parseconfig(const char *filename,
515 struct Configurable *config);
516 static char *my_get_line(FILE *fp);
517 static int create_dir_hierarchy(char *outfile);
519 static void GetStr(char **string,
525 *string = strdup(value);
530 static char *file2string(FILE *file)
539 while(fgets(buffer, sizeof(buffer), file)) {
540 ptr= strchr(buffer, '\r');
543 ptr= strchr(buffer, '\n');
546 stringlen=strlen(buffer);
548 string = realloc(string, len+stringlen+1);
550 string = malloc(stringlen+1);
552 strcpy(string+len, buffer);
559 return NULL; /* no string */
562 static char *file2memory(FILE *file, long *size)
566 char *newstring=NULL;
571 while((len = fread(buffer, 1, sizeof(buffer), file))) {
573 newstring = realloc(string, len+stringlen);
577 break; /* no more strings attached! :-) */
580 string = malloc(len);
581 memcpy(&string[stringlen], buffer, len);
588 return NULL; /* no string */
591 static void clean_getout(struct Configurable *config)
593 struct getout *node=config->url_list;
606 node = next; /* GOTO next */
610 static struct getout *new_getout(struct Configurable *config)
612 struct getout *node =malloc(sizeof(struct getout));
613 struct getout *last= config->url_last;
615 /* clear the struct */
616 memset(node, 0, sizeof(struct getout));
618 /* append this new node last in the list */
622 config->url_list = node; /* first node */
624 /* move the last pointer */
625 config->url_last = node;
630 /* Structure for storing the information needed to build a multiple files
634 struct curl_forms form;
635 struct multi_files *next;
638 /* Add a new list entry possibly with a type_name
640 static struct multi_files *
641 AddMultiFiles (const char *file_name,
642 const char *type_name,
643 const char *show_filename,
644 struct multi_files **multi_start,
645 struct multi_files **multi_current)
647 struct multi_files *multi;
648 struct multi_files *multi_type = NULL;
649 struct multi_files *multi_name = NULL;
650 multi = (struct multi_files *)malloc(sizeof(struct multi_files));
652 memset(multi, 0, sizeof(struct multi_files));
653 multi->form.option = CURLFORM_FILE;
654 multi->form.value = file_name;
660 *multi_start = multi;
663 multi_type = (struct multi_files *)malloc(sizeof(struct multi_files));
665 memset(multi_type, 0, sizeof(struct multi_files));
666 multi_type->form.option = CURLFORM_CONTENTTYPE;
667 multi_type->form.value = type_name;
668 multi->next = multi_type;
678 multi_name = (struct multi_files *)malloc(sizeof(struct multi_files));
680 memset(multi_name, 0, sizeof(struct multi_files));
681 multi_name->form.option = CURLFORM_FILENAME;
682 multi_name->form.value = show_filename;
683 multi->next = multi_name;
694 (*multi_current)->next = multi;
696 *multi_current = multi;
698 return *multi_current;
701 /* Free the items of the list.
703 static void FreeMultiInfo (struct multi_files *multi_start)
705 struct multi_files *multi;
706 while (multi_start) {
708 multi_start = multi_start->next;
713 /***************************************************************************
717 * Reads a 'name=value' paramter and builds the appropriate linked list.
719 * Specify files to upload with 'name=@filename'. Supports specified
720 * given Content-Type of the files. Such as ';type=<content-type>'.
722 * You may specify more than one file for a single name (field). Specify
723 * multiple files by writing it like:
725 * 'name=@filename,filename2,filename3'
727 * If you want content-types specified for each too, write them like:
729 * 'name=@filename;type=image/gif,filename2,filename3'
731 * If you want custom headers added for a single part, write them in a separate
732 * file and do like this:
734 * 'name=foo;headers=@headerfile' or why not
735 * 'name=@filemame;headers=@headerfile'
737 * To upload a file, but to fake the file name that will be included in the
738 * formpost, do like this:
740 * 'name=@filename;filename=/dev/null'
742 * This function uses curl_formadd to fulfill it's job. Is heavily based on
743 * the old curl_formparse code.
745 ***************************************************************************/
747 #define FORM_FILE_SEPARATOR ','
748 #define FORM_TYPE_SEPARATOR ';'
750 static int formparse(char *input,
751 struct curl_httppost **httppost,
752 struct curl_httppost **last_post)
754 /* nextarg MUST be a string in the format 'name=contents' and we'll
755 build a linked list with the info */
761 const char *type = NULL;
765 /* Preallocate contents to the length of input to make sure we don't
766 overwrite anything. */
767 contents = malloc(strlen(input));
768 contents[0] = '\000';
770 if(1 <= sscanf(input, "%255[^=]=%[^\n]", name, contents)) {
771 /* the input was using the correct format */
774 if('@' == contp[0]) {
775 struct multi_files *multi_start = NULL, *multi_current = NULL;
776 /* we use the @-letter to indicate file name(s) */
779 multi_start = multi_current=NULL;
782 /* since this was a file, it may have a content-type specifier
783 at the end too, or a filename. Or both. */
787 sep=strchr(contp, FORM_TYPE_SEPARATOR);
788 sep2=strchr(contp, FORM_FILE_SEPARATOR);
790 /* pick the closest */
791 if(sep2 && (sep2 < sep)) {
794 /* no type was specified! */
801 /* if we got here on a comma, don't do much */
802 if(FORM_FILE_SEPARATOR == *sep)
807 *sep=0; /* terminate file name at separator */
809 while(ptr && (FORM_FILE_SEPARATOR!= *ptr)) {
811 /* pass all white spaces */
812 while(isspace((int)*ptr))
815 if(curl_strnequal("type=", ptr, 5)) {
817 /* set type pointer */
820 /* verify that this is a fine type specifier */
821 if(2 != sscanf(type, "%127[^/]/%127[^;,\n]",
823 fprintf(stderr, "Illegally formatted content-type field!\n");
825 FreeMultiInfo (multi_start);
826 return 2; /* illegal content-type syntax! */
828 /* now point beyond the content-type specifier */
829 sep = (char *)type + strlen(major)+strlen(minor)+1;
831 *sep=0; /* zero terminate type string */
835 else if(curl_strnequal("filename=", ptr, 9)) {
837 ptr=strchr(filename, FORM_TYPE_SEPARATOR);
839 ptr=strchr(filename, FORM_FILE_SEPARATOR);
842 *ptr=0; /* zero terminate */
847 /* confusion, bail out of loop */
850 /* find the following comma */
852 sep=strchr(ptr, FORM_FILE_SEPARATOR);
857 sep=strchr(contp, FORM_FILE_SEPARATOR);
860 /* the next file name starts here */
864 /* if type == NULL curl_formadd takes care of the problem */
866 if (!AddMultiFiles (contp, type, filename, &multi_start,
868 fprintf(stderr, "Error building form post!\n");
870 FreeMultiInfo (multi_start);
873 contp = sep; /* move the contents pointer to after the separator */
875 } while(sep && *sep); /* loop if there's another file name */
877 /* now we add the multiple files section */
879 struct curl_forms *forms = NULL;
880 struct multi_files *ptr = multi_start;
881 unsigned int i, count = 0;
887 (struct curl_forms *)malloc((count+1)*sizeof(struct curl_forms));
890 fprintf(stderr, "Error building form post!\n");
892 FreeMultiInfo (multi_start);
895 for (i = 0, ptr = multi_start; i < count; ++i, ptr = ptr->next)
897 forms[i].option = ptr->form.option;
898 forms[i].value = ptr->form.value;
900 forms[count].option = CURLFORM_END;
901 FreeMultiInfo (multi_start);
902 if (curl_formadd (httppost, last_post,
903 CURLFORM_COPYNAME, name,
904 CURLFORM_ARRAY, forms, CURLFORM_END) != 0) {
905 fprintf(stderr, "curl_formadd failed!\n");
914 if( contp[0]=='<' ) {
915 if (curl_formadd (httppost, last_post,
916 CURLFORM_COPYNAME, name,
917 CURLFORM_FILECONTENT, contp+1, CURLFORM_END) != 0) {
918 fprintf(stderr, "curl_formadd failed!\n");
924 if (curl_formadd (httppost, last_post,
925 CURLFORM_COPYNAME, name,
926 CURLFORM_COPYCONTENTS, contp, CURLFORM_END) != 0) {
927 fprintf(stderr, "curl_formadd failed!\n");
936 fprintf(stderr, "Illegally formatted input field!\n");
947 PARAM_OPTION_AMBIGUOUS,
948 PARAM_OPTION_UNKNOWN,
949 PARAM_REQUIRES_PARAMETER,
951 PARAM_HELP_REQUESTED,
952 PARAM_GOT_EXTRA_PARAMETER,
954 PARAM_LIBCURL_DOESNT_SUPPORT,
958 static const char *param2text(int res)
960 ParameterError error = (ParameterError)res;
962 case PARAM_GOT_EXTRA_PARAMETER:
963 return "had unsupported trailing garbage";
964 case PARAM_OPTION_UNKNOWN:
966 case PARAM_OPTION_AMBIGUOUS:
967 return "is ambiguous";
968 case PARAM_REQUIRES_PARAMETER:
969 return "requires parameter";
971 return "is badly used here";
972 case PARAM_BAD_NUMERIC:
973 return "expected a proper numerical parameter";
974 case PARAM_LIBCURL_DOESNT_SUPPORT:
975 return "the installed libcurl version doesn't support this";
977 return "unknown error";
981 static void cleanarg(char *str)
983 #ifdef HAVE_WRITABLE_ARGV
984 /* now that GetStr has copied the contents of nextarg, wipe the next
985 * argument out so that the username:password isn't displayed in the
986 * system process list */
988 size_t len = strlen(str);
989 memset(str, ' ', len);
997 * Parse the string and write the integer in the given address. Return
998 * non-zero on failure, zero on success.
1000 * The string must start with a digit to be valid.
1003 static int str2num(long *val, char *str)
1006 if(isdigit((int)*str))
1009 retcode = 1; /* badness */
1014 * Parses the given string looking for an offset (which may be
1015 * a larger-than-integer value).
1017 * @param val the offset to populate
1018 * @param str the buffer containing the offset
1019 * @return zero if successful, non-zero if failure.
1021 static int str2offset(curl_off_t *val, char *str)
1023 #if SIZEOF_CURL_OFF_T > 4
1024 /* Ugly, but without going through a bunch of rigmarole, we don't have the
1025 * definitions for LLONG_{MIN,MAX} or LONG_LONG_{MIN,MAX}.
1029 #define LLONG_MAX (curl_off_t)0x7FFFFFFFFFFFFFFFi64
1030 #define LLONG_MIN (curl_off_t)0x8000000000000000i64
1032 #define LLONG_MAX (curl_off_t)0x7FFFFFFFFFFFFFFFLL
1033 #define LLONG_MIN (curl_off_t)0x8000000000000000LL
1037 /* this is a duplicate of the function that is also used in libcurl */
1038 *val = strtoofft(str, NULL, 0);
1040 if ((*val == LLONG_MAX || *val == LLONG_MIN) && errno == ERANGE)
1043 *val = strtol(str, NULL, 0);
1044 if ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE)
1050 static void checkpasswd(const char *kind, /* for what purpose */
1051 char **userpwd) /* pointer to allocated string */
1053 char *ptr = strchr(*userpwd, ':');
1055 /* no password present, prompt for one */
1056 char passwd[256]="";
1059 size_t userlen = strlen(*userpwd);
1062 /* build a nice-looking prompt */
1063 curl_msnprintf(prompt, sizeof(prompt),
1064 "Enter %s password for user '%s':",
1068 getpass_r(prompt, passwd, sizeof(passwd));
1069 passwdlen = strlen(passwd);
1071 /* extend the allocated memory area to fit the password too */
1072 passptr = realloc(*userpwd,
1073 passwdlen + 1 + /* an extra for the colon */
1074 userlen + 1); /* an extra for the zero */
1077 /* append the password separated with a colon */
1078 passptr[userlen]=':';
1079 memcpy(&passptr[userlen+1], passwd, passwdlen+1);
1085 static ParameterError getparameter(char *flag, /* f or -long-flag */
1086 char *nextarg, /* NULL if unset */
1087 bool *usedarg, /* set to TRUE if the arg
1089 struct Configurable *config)
1092 char subletter=0; /* subletters can only occur on long options */
1094 const char *parse=NULL;
1099 bool singleopt=FALSE; /* when true means '-o foo' used '-ofoo' */
1104 boolean whether it takes an additional argument
1106 struct LongShort aliases[]= {
1107 /* all these ones, starting with "*" or "$" as a short-option have *no*
1108 short option to mention. */
1110 {"*a", "random-file", TRUE},
1111 {"*b", "egd-file", TRUE},
1112 {"*c", "connect-timeout", TRUE},
1113 {"*d", "ciphers", TRUE},
1114 {"*e", "disable-epsv", FALSE},
1115 #ifdef USE_ENVIRONMENT
1116 {"*f", "environment", FALSE},
1118 {"*g", "trace", TRUE},
1119 {"*h", "trace-ascii", TRUE},
1120 {"*i", "limit-rate", TRUE},
1121 {"*j", "compressed", FALSE}, /* might take an arg someday */
1122 {"*k", "digest", FALSE},
1123 {"*l", "negotiate", FALSE},
1124 {"*m", "ntlm", FALSE},
1125 {"*n", "basic", FALSE},
1126 {"*o", "anyauth", FALSE},
1128 {"*p", "wdebug", FALSE},
1130 {"*q", "ftp-create-dirs", FALSE},
1131 {"*r", "create-dirs", FALSE},
1132 {"*s", "max-redirs", TRUE},
1133 {"*t", "proxy-ntlm", FALSE},
1134 {"*u", "crlf", FALSE},
1135 {"*v", "stderr", TRUE},
1136 {"*w", "interface", TRUE},
1137 {"*x", "krb4", TRUE},
1138 {"*y", "max-filesize", TRUE},
1139 {"*z", "disable-eprt", FALSE},
1140 {"$a", "ftp-ssl", FALSE},
1141 {"$b", "ftp-pasv", FALSE},
1142 {"$c", "socks5", TRUE},
1143 {"0", "http1.0", FALSE},
1144 {"1", "tlsv1", FALSE},
1145 {"2", "sslv2", FALSE},
1146 {"3", "sslv3", FALSE},
1147 {"4", "ipv4", FALSE},
1148 {"6", "ipv6", FALSE},
1149 {"a", "append", FALSE},
1150 {"A", "user-agent", TRUE},
1151 {"b", "cookie", TRUE},
1152 {"B", "use-ascii", FALSE},
1153 {"c", "cookie-jar", TRUE},
1154 {"C", "continue-at", TRUE},
1155 {"d", "data", TRUE},
1156 {"da", "data-ascii", TRUE},
1157 {"db", "data-binary", TRUE},
1158 {"D", "dump-header", TRUE},
1159 {"e", "referer", TRUE},
1160 {"E", "cert", TRUE},
1161 {"Ea", "cacert", TRUE},
1162 {"Eb","cert-type", TRUE},
1164 {"Ed","key-type", TRUE},
1165 {"Ee","pass", TRUE},
1166 {"Ef","engine", TRUE},
1167 {"Eg","capath ", TRUE},
1168 {"f", "fail", FALSE},
1169 {"F", "form", TRUE},
1170 {"g", "globoff", FALSE},
1171 {"G", "get", FALSE},
1172 {"h", "help", FALSE},
1173 {"H", "header", TRUE},
1174 {"i", "include", FALSE},
1175 {"I", "head", FALSE},
1176 {"j", "junk-session-cookies", FALSE},
1177 {"k", "insecure", FALSE},
1178 {"K", "config", TRUE},
1179 {"l", "list-only", FALSE},
1180 {"L", "location", FALSE},
1181 {"Lt", "location-trusted", FALSE},
1182 {"m", "max-time", TRUE},
1183 {"M", "manual", FALSE},
1184 {"n", "netrc", FALSE},
1185 {"no", "netrc-optional", FALSE},
1186 {"N", "no-buffer", FALSE},
1187 {"o", "output", TRUE},
1188 {"O", "remote-name", FALSE},
1189 {"p", "proxytunnel", FALSE},
1190 {"P", "ftpport", TRUE}, /* older version */
1191 {"P", "ftp-port", TRUE},
1192 {"q", "disable", FALSE},
1193 {"Q", "quote", TRUE},
1194 {"r", "range", TRUE},
1195 {"R", "remote-time", FALSE},
1196 {"s", "silent", FALSE},
1197 {"S", "show-error", FALSE},
1198 {"t", "telnet-options", TRUE},
1199 {"T", "upload-file", TRUE},
1200 {"u", "user", TRUE},
1201 {"U", "proxy-user", TRUE},
1202 {"v", "verbose", FALSE},
1203 {"V", "version", FALSE},
1204 {"w", "write-out", TRUE},
1205 {"x", "proxy", TRUE},
1206 {"X", "request", TRUE},
1207 {"X", "http-request", TRUE}, /* OBSOLETE VERSION */
1208 {"Y", "speed-limit", TRUE},
1209 {"y", "speed-time", TRUE},
1210 {"z", "time-cond", TRUE},
1211 {"#", "progress-bar",FALSE},
1214 if(('-' != flag[0]) ||
1215 (('-' == flag[0]) && ('-' == flag[1]))) {
1216 /* this should be a long name */
1217 char *word=('-' == flag[0])?flag+2:flag;
1218 size_t fnam=strlen(word);
1220 for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
1221 if(curl_strnequal(aliases[j].lname, word, fnam)) {
1224 if(curl_strequal(aliases[j].lname, word)) {
1225 parse = aliases[j].letter;
1227 numhits = 1; /* a single unique hit */
1230 parse = aliases[j].letter;
1235 /* this is at least the second match! */
1236 return PARAM_OPTION_AMBIGUOUS;
1239 return PARAM_OPTION_UNKNOWN;
1243 flag++; /* prefixed with one dash, pass it */
1249 /* we can loop here if we have multiple single-letters */
1252 letter = parse?(char)*parse:'\0';
1255 subletter = parse[1];
1257 *usedarg = FALSE; /* default is that we don't use the arg */
1260 fprintf(stderr, "OPTION: %c %s\n", letter, nextarg?nextarg:"<null>");
1263 for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
1264 if(letter == aliases[j].letter[0]) {
1270 return PARAM_OPTION_UNKNOWN;
1274 return PARAM_OPTION_UNKNOWN;
1276 if(!longopt && aliases[hit].extraparam && parse[1]) {
1277 nextarg=(char *)&parse[1]; /* this is the actual extra parameter */
1278 singleopt=TRUE; /* don't loop anymore after this */
1280 else if(!nextarg && aliases[hit].extraparam) {
1281 return PARAM_REQUIRES_PARAMETER;
1283 else if(nextarg && aliases[hit].extraparam)
1284 *usedarg = TRUE; /* mark it as used */
1287 case '*': /* options without a short option */
1289 case 'a': /* random-file */
1290 GetStr(&config->random_file, nextarg);
1292 case 'b': /* egd-file */
1293 GetStr(&config->egd_file, nextarg);
1295 case 'c': /* connect-timeout */
1296 if(str2num(&config->connecttimeout, nextarg))
1297 return PARAM_BAD_NUMERIC;
1299 case 'd': /* ciphers */
1300 GetStr(&config->cipher_list, nextarg);
1302 case 'e': /* --disable-epsv */
1303 config->disable_epsv ^= TRUE;
1305 #ifdef USE_ENVIRONMENT
1307 config->writeenv ^= TRUE;
1310 case 'g': /* --trace */
1311 GetStr(&config->trace_dump, nextarg);
1313 case 'h': /* --trace-ascii */
1314 GetStr(&config->trace_dump, nextarg);
1315 config->trace_ascii = TRUE;
1317 case 'i': /* --limit-rate */
1319 /* We support G, M, K too */
1321 unsigned long value = strtol(nextarg, &unit, 0);
1322 switch(nextarg[strlen(nextarg)-1]) {
1325 value *= 1024*1024*1024;
1336 config->recvpersecond = value;
1337 config->sendpersecond = value;
1341 case 'j': /* --compressed */
1342 config->encoding ^= TRUE;
1345 case 'k': /* --digest */
1346 config->authtype = CURLAUTH_DIGEST;
1349 case 'l': /* --negotiate */
1350 if(curlinfo->features & CURL_VERSION_GSSNEGOTIATE)
1351 config->authtype = CURLAUTH_GSSNEGOTIATE;
1353 return PARAM_LIBCURL_DOESNT_SUPPORT;
1356 case 'm': /* --ntlm */
1357 if(curlinfo->features & CURL_VERSION_NTLM)
1358 config->authtype = CURLAUTH_NTLM;
1360 return PARAM_LIBCURL_DOESNT_SUPPORT;
1363 case 'n': /* --basic for completeness */
1364 config->authtype = CURLAUTH_BASIC;
1367 case 'o': /* --anyauth, let libcurl pick it */
1368 config->authtype = CURLAUTH_ANY;
1372 case 'p': /* --wdebug */
1376 case 'q': /* --ftp-create-dirs */
1377 config->ftp_create_dirs ^= TRUE;
1380 case 'r': /* --create-dirs */
1381 config->create_dirs = TRUE;
1384 case 's': /* --max-redirs */
1385 /* specified max no of redirects (http(s)) */
1386 if(str2num(&config->maxredirs, nextarg))
1387 return PARAM_BAD_NUMERIC;
1390 case 't': /* --proxy-ntlm */
1391 config->proxyntlm ^= TRUE;
1394 case 'u': /* --crlf */
1395 /* LF -> CRLF conversinon? */
1396 config->crlf = TRUE;
1399 case 'v': /* --stderr */
1400 if(strcmp(nextarg, "-")) {
1401 config->errors = fopen(nextarg, "wt");
1402 config->errors_fopened = TRUE;
1405 config->errors = stdout;
1407 case 'w': /* --interface */
1409 GetStr(&config->iface, nextarg);
1411 case 'x': /* --krb4 */
1412 /* krb4 level string */
1413 if(curlinfo->features & CURL_VERSION_KERBEROS4)
1414 GetStr(&config->krb4level, nextarg);
1416 return PARAM_LIBCURL_DOESNT_SUPPORT;
1418 case 'y': /* --max-filesize */
1419 if(str2offset(&config->max_filesize, nextarg))
1420 return PARAM_BAD_NUMERIC;
1422 case 'z': /* --disable-eprt */
1423 config->disable_eprt ^= TRUE;
1426 default: /* the URL! */
1429 if(config->url_get || (config->url_get=config->url_list)) {
1430 /* there's a node here, if it already is filled-in continue to find
1432 while(config->url_get && (config->url_get->flags&GETOUT_URL))
1433 config->url_get = config->url_get->next;
1436 /* now there might or might not be an available node to fill in! */
1440 url = config->url_get;
1442 /* there was no free node, create one! */
1443 url=new_getout(config);
1446 /* fill in the URL */
1447 GetStr(&url->url, nextarg);
1448 url->flags |= GETOUT_URL;
1453 case '$': /* more options without a short option */
1455 case 'a': /* --ftp-ssl */
1456 config->ftp_ssl ^= TRUE;
1458 case 'b': /* --ftp-pasv */
1460 free(config->ftpport);
1461 config->ftpport = NULL;
1463 case 'c': /* --socks specifies a socks5 proxy to use */
1464 GetStr(&config->socks5proxy, nextarg);
1468 case '#': /* added 19990617 larsa */
1469 config->progressmode ^= CURL_PROGRESS_BAR;
1472 /* HTTP version 1.0 */
1473 config->httpversion = CURL_HTTP_VERSION_1_0;
1477 config->ssl_version = CURL_SSLVERSION_TLSv1;
1481 config->ssl_version = CURL_SSLVERSION_SSLv2;
1485 config->ssl_version = CURL_SSLVERSION_SSLv3;
1489 config->ip_version = 4;
1493 config->ip_version = 6;
1496 /* This makes the FTP sessions use APPE instead of STOR */
1497 config->conf ^= CONF_FTPAPPEND;
1500 /* This specifies the User-Agent name */
1501 GetStr(&config->useragent, nextarg);
1503 case 'b': /* cookie string coming up: */
1504 if(nextarg[0] == '@') {
1507 else if(strchr(nextarg, '=')) {
1508 /* A cookie string must have a =-letter */
1509 GetStr(&config->cookie, nextarg);
1512 /* We have a cookie file to read from! */
1513 GetStr(&config->cookiefile, nextarg);
1516 /* use ASCII/text when transfering */
1517 config->conf ^= CONF_GETTEXT;
1520 /* get the file name to dump all cookies in */
1521 GetStr(&config->cookiejar, nextarg);
1524 /* This makes us continue an ftp transfer at given position */
1525 if(!curl_strequal(nextarg, "-")) {
1526 if(str2offset(&config->resume_from, nextarg))
1527 return PARAM_BAD_NUMERIC;
1528 config->resume_from_current = FALSE;
1531 config->resume_from_current = TRUE;
1532 config->resume_from = 0;
1534 config->use_resume=TRUE;
1537 /* postfield data */
1539 char *postdata=NULL;
1541 if('@' == *nextarg) {
1542 /* the data begins with a '@' letter, it means that a file name
1543 or - (stdin) follows */
1546 nextarg++; /* pass the @ */
1548 if(curl_strequal("-", nextarg))
1551 file = fopen(nextarg, "rb");
1553 if(subletter == 'b') /* forced binary */
1554 postdata = file2memory(file, &config->postfieldsize);
1556 postdata = file2string(file);
1557 if(file && (file != stdin))
1561 GetStr(&postdata, nextarg);
1564 if(config->postfields) {
1565 /* we already have a string, we append this one
1566 with a separating &-letter */
1567 char *oldpost=config->postfields;
1568 config->postfields=aprintf("%s&%s", oldpost, postdata);
1573 config->postfields=postdata;
1576 We can't set the request type here, as this data might be used in
1577 a simple GET if -G is used. Already or soon.
1579 if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
1580 return PARAM_BAD_USE;
1584 /* dump-header to given file name */
1585 GetStr(&config->headerfile, nextarg);
1589 char *ptr = strstr(nextarg, ";auto");
1591 /* Automatic referer requested, this may be combined with a
1593 config->conf |= CONF_AUTO_REFERER;
1594 *ptr = 0; /* zero terminate here */
1596 GetStr(&config->referer, nextarg);
1601 case 'a': /* CA info PEM file */
1602 /* CA info PEM file */
1603 GetStr(&config->cacert, nextarg);
1605 case 'b': /* cert file type */
1606 GetStr(&config->cert_type, nextarg);
1608 case 'c': /* private key file */
1609 GetStr(&config->key, nextarg);
1611 case 'd': /* private key file type */
1612 GetStr(&config->key_type, nextarg);
1614 case 'e': /* private key passphrase */
1615 GetStr(&config->key_passwd, nextarg);
1618 case 'f': /* crypto engine */
1619 GetStr(&config->engine, nextarg);
1621 case 'g': /* CA info PEM file */
1622 /* CA cert directory */
1623 GetStr(&config->capath, nextarg);
1625 default: /* certificate file */
1627 char *ptr = strchr(nextarg, ':');
1628 /* Since we live in a world of weirdness and confusion, the win32
1629 dudes can use : when using drive letters and thus
1630 c:\file:password needs to work. In order not to break
1631 compatibility, we still use : as separator, but we try to detect
1632 when it is used for a file name! On windows. */
1635 (ptr == &nextarg[1]) &&
1636 (nextarg[2] == '\\') &&
1637 (isalpha((int)nextarg[0])) )
1638 /* colon in the second column, followed by a backslash, and the
1639 first character is an alphabetic letter:
1641 this is a drive letter colon */
1642 ptr = strchr(&nextarg[3], ':'); /* find the next one instead */
1645 /* we have a password too */
1648 GetStr(&config->key_passwd, ptr);
1650 GetStr(&config->cert, nextarg);
1656 /* fail hard on errors */
1657 config->conf ^= CONF_FAILONERROR;
1660 /* "form data" simulation, this is a little advanced so lets do our best
1661 to sort this out slowly and carefully */
1662 if(formparse(nextarg,
1664 &config->last_post))
1665 return PARAM_BAD_USE;
1666 if(SetHTTPrequest(HTTPREQ_POST, &config->httpreq))
1667 return PARAM_BAD_USE;
1670 case 'g': /* g disables URLglobbing */
1671 config->globoff ^= TRUE;
1674 case 'G': /* HTTP GET */
1675 config->use_httpget = TRUE;
1678 case 'h': /* h for help */
1680 return PARAM_HELP_REQUESTED;
1682 /* A custom header to append to a list */
1683 config->headers = curl_slist_append(config->headers, nextarg);
1686 config->conf ^= CONF_HEADER; /* include the HTTP header as well */
1689 config->cookiesession ^= TRUE;
1693 * This is a bit tricky. We either SET both bits, or we clear both
1694 * bits. Let's not make any other outcomes from this.
1696 if((CONF_HEADER|CONF_NOBODY) !=
1697 (config->conf&(CONF_HEADER|CONF_NOBODY)) ) {
1698 /* one of them weren't set, set both */
1699 config->conf |= (CONF_HEADER|CONF_NOBODY);
1700 if(SetHTTPrequest(HTTPREQ_HEAD, &config->httpreq))
1701 return PARAM_BAD_USE;
1704 /* both were set, clear both */
1705 config->conf &= ~(CONF_HEADER|CONF_NOBODY);
1706 if(SetHTTPrequest(HTTPREQ_GET, &config->httpreq))
1707 return PARAM_BAD_USE;
1710 case 'k': /* allow insecure SSL connects */
1711 config->insecure_ok ^= TRUE;
1713 case 'K': /* parse config file */
1714 parseconfig(nextarg, config);
1717 config->conf ^= CONF_FTPLISTONLY; /* only list the names of the FTP dir */
1720 config->conf ^= CONF_FOLLOWLOCATION; /* Follow Location: HTTP headers */
1721 switch (subletter) {
1723 /* Continue to send authentication (user+password) when following
1724 * locations, even when hostname changed */
1725 config->conf ^= CONF_UNRESTRICTED_AUTH;
1730 /* specified max time */
1731 if(str2num(&config->timeout, nextarg))
1732 return PARAM_BAD_NUMERIC;
1734 case 'M': /* M for manual, huge help */
1737 return PARAM_HELP_REQUESTED;
1740 "curl: built-in manual was disabled at build-time!\n");
1741 return PARAM_OPTION_UNKNOWN;
1745 case 'o': /* CA info PEM file */
1746 /* use .netrc or URL */
1747 config->conf ^= CONF_NETRC_OPT;
1750 /* pick info from .netrc, if this is used for http, curl will
1751 automatically enfore user+password with the request */
1752 config->conf ^= CONF_NETRC;
1757 /* disable the output I/O buffering */
1758 config->nobuffer ^= 1;
1765 if(config->url_out || (config->url_out=config->url_list)) {
1766 /* there's a node here, if it already is filled-in continue to find
1768 while(config->url_out && (config->url_out->flags&GETOUT_OUTFILE))
1769 config->url_out = config->url_out->next;
1772 /* now there might or might not be an available node to fill in! */
1776 url = config->url_out;
1778 /* there was no free node, create one! */
1779 url=new_getout(config);
1782 /* fill in the outfile */
1784 GetStr(&url->outfile, nextarg);
1786 url->outfile=NULL; /* leave it */
1787 url->flags |= GETOUT_USEREMOTE;
1789 url->flags |= GETOUT_OUTFILE;
1794 /* This makes the FTP sessions use PORT instead of PASV */
1795 /* use <eth0> or <192.168.10.10> style addresses. Anything except
1796 this will make us try to get the "default" address.
1797 NOTE: this is a changed behaviour since the released 4.1!
1799 GetStr(&config->ftpport, nextarg);
1802 /* proxy tunnel for non-http protocols */
1803 config->proxytunnel ^= TRUE;
1806 case 'q': /* if used first, already taken care of, we do it like
1807 this so we don't cause an error! */
1810 /* QUOTE command to send to FTP server */
1811 switch(nextarg[0]) {
1813 /* prefixed with a dash makes it a POST TRANSFER one */
1815 config->postquote = curl_slist_append(config->postquote, nextarg);
1818 /* prefixed with a plus makes it a just-before-transfer one */
1820 config->prequote = curl_slist_append(config->prequote, nextarg);
1823 config->quote = curl_slist_append(config->quote, nextarg);
1827 /* byte range requested */
1828 GetStr(&config->range, nextarg);
1831 /* use remote file's time */
1832 config->remote_time ^= TRUE;
1835 /* don't show progress meter, don't show errors : */
1836 config->conf |= (CONF_MUTE|CONF_NOPROGRESS);
1837 config->showerror ^= TRUE; /* toggle off */
1841 config->showerror ^= TRUE; /* toggle on if used with -s */
1844 /* Telnet options */
1845 config->telnet_options =
1846 curl_slist_append(config->telnet_options, nextarg);
1849 /* we are uploading */
1852 if(config->url_out || (config->url_out=config->url_list)) {
1853 /* there's a node here, if it already is filled-in continue to find
1855 while(config->url_out && (config->url_out->flags&GETOUT_UPLOAD))
1856 config->url_out = config->url_out->next;
1859 /* now there might or might not be an available node to fill in! */
1863 url = config->url_out;
1865 /* there was no free node, create one! */
1866 url=new_getout(config);
1869 url->flags |= GETOUT_UPLOAD; /* mark -T used */
1871 url->flags |= GETOUT_NOUPLOAD;
1873 /* "-" equals stdin, but keep the string around for now */
1874 GetStr(&url->infile, nextarg);
1881 GetStr(&config->userpwd, nextarg);
1883 checkpasswd("host", &config->userpwd);
1886 /* Proxy user:password */
1887 GetStr(&config->proxyuserpwd, nextarg);
1889 checkpasswd("proxy", &config->proxyuserpwd);
1892 config->conf ^= CONF_VERBOSE; /* talk a lot */
1898 printf(CURL_ID "%s\n", curl_version());
1899 if (curlinfo->protocols) {
1900 printf("Protocols: ");
1901 for (proto=curlinfo->protocols; *proto; ++proto) {
1902 printf("%s ", *proto);
1904 puts(""); /* newline */
1906 if(curlinfo->features) {
1912 struct feat feats[] = {
1913 {"IPv6", CURL_VERSION_IPV6},
1914 {"krb4", CURL_VERSION_KERBEROS4},
1915 {"SSL", CURL_VERSION_SSL},
1916 {"libz", CURL_VERSION_LIBZ},
1917 {"NTLM", CURL_VERSION_NTLM},
1918 {"GSS-Negotiate", CURL_VERSION_GSSNEGOTIATE},
1919 {"Debug", CURL_VERSION_DEBUG},
1920 {"AsynchDNS", CURL_VERSION_ASYNCHDNS},
1921 {"SPNEGO", CURL_VERSION_SPNEGO},
1922 {"Largefile", CURL_VERSION_LARGEFILE}
1924 printf("Features: ");
1925 for(i=0; i<sizeof(feats)/sizeof(feats[0]); i++) {
1926 if(curlinfo->features & feats[i].bitmask)
1927 printf("%s ", feats[i].name);
1929 puts(""); /* newline */
1932 return PARAM_HELP_REQUESTED;
1934 /* get the output string */
1935 if('@' == *nextarg) {
1936 /* the data begins with a '@' letter, it means that a file name
1937 or - (stdin) follows */
1939 nextarg++; /* pass the @ */
1940 if(curl_strequal("-", nextarg))
1943 file = fopen(nextarg, "r");
1944 config->writeout = file2string(file);
1945 if(file && (file != stdin))
1949 GetStr(&config->writeout, nextarg);
1953 GetStr(&config->proxy, nextarg);
1956 /* set custom request */
1957 GetStr(&config->customrequest, nextarg);
1960 /* low speed time */
1961 if(str2num(&config->low_speed_time, nextarg))
1962 return PARAM_BAD_NUMERIC;
1963 if(!config->low_speed_limit)
1964 config->low_speed_limit = 1;
1967 /* low speed limit */
1968 if(str2num(&config->low_speed_limit, nextarg))
1969 return PARAM_BAD_NUMERIC;
1970 if(!config->low_speed_time)
1971 config->low_speed_time=30;
1973 case 'z': /* time condition coming up */
1978 /* If-Modified-Since: (section 14.28 in RFC2068) */
1979 config->timecond = CURL_TIMECOND_IFMODSINCE;
1982 /* If-Unmodified-Since: (section 14.24 in RFC2068) */
1983 config->timecond = CURL_TIMECOND_IFUNMODSINCE;
1987 /* Last-Modified: (section 14.29 in RFC2068) */
1988 config->timecond = CURL_TIMECOND_LASTMOD;
1993 config->condtime=curl_getdate(nextarg, &now);
1994 if(-1 == (int)config->condtime) {
1995 /* now let's see if it is a file name to get the time from instead! */
1996 struct stat statbuf;
1997 if(-1 == stat(nextarg, &statbuf)) {
1998 /* failed, remove time condition */
1999 config->timecond = CURL_TIMECOND_NONE;
2002 /* pull the time out from the file */
2003 config->condtime = statbuf.st_mtime;
2007 default: /* unknown flag */
2008 return PARAM_OPTION_UNKNOWN;
2012 } while(!longopt && !singleopt && *++parse && !*usedarg);
2018 static void parseconfig(const char *filename,
2019 struct Configurable *config)
2023 char filebuffer[512];
2027 if(!filename || !*filename) {
2028 /* NULL or no file name attempts to load .curlrc from the homedir! */
2030 #define CURLRC DOT_CHAR "curlrc"
2033 filename = CURLRC; /* sensible default */
2034 home = homedir(); /* portable homedir finder */
2036 if(strlen(home)<(sizeof(filebuffer)-strlen(CURLRC))) {
2037 snprintf(filebuffer, sizeof(filebuffer),
2038 "%s%s%s", home, DIR_CHAR, CURLRC);
2040 filename = filebuffer;
2042 free(home); /* we've used it, now free it */
2045 # else /* AmigaOS */
2046 /* On AmigaOS all the config files are into env:
2048 filename = "ENV:" CURLRC;
2053 if(strcmp(filename,"-"))
2054 file = fopen(filename, "r");
2066 #define isseparator(x) (((x)=='=') || ((x) == ':'))
2068 while (NULL != (aline = my_get_line(file))) {
2071 alloced_param=FALSE;
2073 /* lines with # in the fist column is a comment! */
2074 while(*line && isspace((int)*line))
2088 /* the option keywords starts here */
2090 while(*line && !isspace((int)*line) && !isseparator(*line))
2092 /* ... and has ended here */
2095 *line++=0; /* zero terminate, we have a local copy of the data */
2098 fprintf(stderr, "GOT: %s\n", option);
2101 /* pass spaces and separator(s) */
2102 while(*line && (isspace((int)*line) || isseparator(*line)))
2105 /* the parameter starts here (unless quoted) */
2108 /* quoted parameter, do the qoute dance */
2110 param=strdup(line); /* parameter */
2114 while(*line && (*line != '\"')) {
2119 /* default is to output the letter after the backslah */
2120 switch(out = *line) {
2122 continue; /* this'll break out of the loop */
2142 *ptr=0; /* always zero terminate */
2146 param=line; /* parameter starts here */
2147 while(*line && !isspace((int)*line))
2149 *line=0; /* zero terminate */
2152 if (param && !*param) {
2153 /* do this so getparameter can check for required parameters.
2154 Otherwise it always thinks there's a parameter. */
2161 fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)"));
2163 res = getparameter(option, param, &usedarg, config);
2165 if (param && *param && !usedarg)
2166 /* we passed in a parameter that wasn't used! */
2167 res = PARAM_GOT_EXTRA_PARAMETER;
2169 if(res != PARAM_OK) {
2170 /* the help request isn't really an error */
2171 if(!strcmp(filename, "-")) {
2172 filename=(char *)"<stdin>";
2174 if(PARAM_HELP_REQUESTED != res) {
2175 const char *reason = param2text(res);
2176 fprintf(stderr, "%s:%d: warning: '%s' %s\n",
2177 filename, lineno, option, reason);
2194 static void go_sleep(long ms)
2197 /* portable subsecond "sleep" */
2198 poll((void *)0, 0, ms);
2200 /* systems without poll() need other solutions */
2203 /* Windows offers a millisecond sleep */
2206 /* Other systems must use select() for this */
2207 struct timeval timeout;
2210 timeout.tv_usec = ms * 1000;
2212 select(0, NULL, NULL, NULL, &timeout);
2221 struct Configurable *config;
2224 static int my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
2227 struct OutStruct *out=(struct OutStruct *)stream;
2228 struct Configurable *config = out->config;
2229 if(out && !out->stream) {
2230 /* open file for writing */
2231 out->stream=fopen(out->filename, "wb");
2233 return -1; /* failure */
2236 if(config->recvpersecond) {
2238 * We know when we received data the previous time. We know how much data
2239 * we get now. Make sure that this is not faster than we are told to run.
2240 * If we're faster, sleep a while *before* doing the fwrite() here.
2248 timediff = now - config->lastrecvtime;
2249 if( size*nmemb > config->recvpersecond*timediff) {
2250 /* figure out how many milliseconds to rest */
2251 sleep_time = (size*nmemb)*1000/config->recvpersecond - timediff*1000;
2254 * Make sure we don't sleep for so long that we trigger the speed limit.
2255 * This won't limit the bandwidth quite the way we've been asked to, but
2256 * at least the transfer has a chance.
2258 if (config->low_speed_time > 0)
2259 sleep_time = MIN(sleep_time,(config->low_speed_time * 1000) / 2);
2261 go_sleep (sleep_time);
2264 config->lastrecvtime = now;
2267 rc = fwrite(buffer, size, nmemb, out->stream);
2269 if(config->nobuffer)
2270 /* disable output buffering */
2271 fflush(out->stream);
2278 struct Configurable *config;
2281 static int my_fread(void *buffer, size_t size, size_t nmemb, void *userp)
2283 struct InStruct *in=(struct InStruct *)userp;
2285 struct Configurable *config = in->config;
2287 if(config->sendpersecond) {
2289 * We know when we sent data the previous time. We know how much data
2290 * we sent. Make sure that this was not faster than we are told to run.
2291 * If we're faster, sleep a while *before* doing the fread() here.
2292 * Also, make no larger fread() than should be sent this second!
2299 timediff = now - config->lastsendtime;
2300 if( config->lastsendsize > config->sendpersecond*timediff) {
2301 /* figure out how many milliseconds to rest */
2302 go_sleep ( config->lastsendsize*1000/config->sendpersecond -
2306 config->lastsendtime = now;
2308 if(size*nmemb > config->sendpersecond) {
2309 /* lower the size to actually read */
2310 nmemb = config->sendpersecond;
2313 config->lastsendsize = size*nmemb;
2317 return fread(buffer, size, nmemb, in->stream);
2320 struct ProgressData {
2324 FILE *out; /* where to write everything to */
2325 curl_off_t initial_size;
2328 static int myprogress (void *clientp,
2334 /* The original progress-bar source code was written for curl by Lars Aas,
2335 and this new edition inherits some of his concepts. */
2346 struct ProgressData *bar = (struct ProgressData *)clientp;
2347 double total = dltotal + ultotal + bar->initial_size;
2348 double point = dlnow + ulnow + bar->initial_size; /* we've come this far */
2350 bar->calls++; /* simply count invokes */
2353 int prevblock = (int)bar->prev / 1024;
2354 int thisblock = (int)point / 1024;
2355 while ( thisblock > prevblock ) {
2356 fprintf( bar->out, "#" );
2361 frac = point / total;
2362 percent = frac * 100.0f;
2363 barwidth = bar->width - 7;
2364 num = (int) (((double)barwidth) * frac);
2366 for ( i = 0; i < num; i++ ) {
2370 sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
2371 sprintf( outline, format, line, percent );
2372 fprintf( bar->out, "\r%s", outline );
2381 void progressbarinit(struct ProgressData *bar,
2382 struct Configurable *config)
2390 memset(bar, 0, sizeof(struct ProgressData));
2392 /* pass this through to progress function so
2393 * it can display progress towards total file
2394 * not just the part that's left. (21-may-03, dbyron) */
2395 if (config->use_resume)
2396 bar->initial_size = config->resume_from;
2398 /* TODO: get terminal width through ansi escapes or something similar.
2399 try to update width when xterm is resized... - 19990617 larsa */
2402 * OS/2 users most likely won't have this env var set, and besides that
2403 * we're using our own way to determine screen width */
2404 colp = curl_getenv("COLUMNS");
2406 bar->width = atoi(colp);
2413 * We use this emx library call to get the screen width, and subtract
2414 * one from what we got in order to avoid a problem with the cursor
2415 * advancing to the next line if we print a string that is as long as
2416 * the screen is wide. */
2419 bar->width = scr_size[0] - 1;
2422 bar->out = config->errors;
2426 void dump(const char *text,
2427 FILE *stream, unsigned char *ptr, size_t size,
2433 unsigned int width=0x10;
2436 /* without the hex output, we can fit more on screen */
2439 fprintf(stream, "%s, %zd bytes (0x%zx)\n", text, size, size);
2441 for(i=0; i<size; i+= width) {
2443 fprintf(stream, "%04zx: ", i);
2446 /* hex not disabled, show it */
2447 for(c = 0; c < width; c++)
2449 fprintf(stream, "%02x ", ptr[i+c]);
2454 for(c = 0; (c < width) && (i+c < size); c++) {
2455 /* check for 0D0A; if found, skip past and start a new line of output */
2456 if (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
2460 fprintf(stream, "%c",
2461 (ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.');
2462 /* check again for 0D0A, to avoid an extra \n if it's at width */
2463 if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
2468 fputc('\n', stream); /* newline */
2474 int my_trace(CURL *handle, curl_infotype type,
2475 unsigned char *data, size_t size,
2478 struct Configurable *config = (struct Configurable *)userp;
2479 FILE *output=config->errors;
2482 (void)handle; /* prevent compiler warning */
2484 if(!config->trace_stream) {
2485 /* open for append */
2486 if(curl_strequal("-", config->trace_dump))
2487 config->trace_stream = stdout;
2489 config->trace_stream = fopen(config->trace_dump, "w");
2490 config->trace_fopened = TRUE;
2494 if(config->trace_stream)
2495 output = config->trace_stream;
2499 fprintf(output, "== Info: %s", data);
2500 default: /* in case a new one is introduced to shock us */
2503 case CURLINFO_HEADER_OUT:
2504 text = "=> Send header";
2506 case CURLINFO_DATA_OUT:
2507 text = "=> Send data";
2509 case CURLINFO_HEADER_IN:
2510 text = "<= Recv header";
2512 case CURLINFO_DATA_IN:
2513 text = "<= Recv data";
2517 dump(text, output, data, size, config->trace_ascii);
2521 static void free_config_fields(struct Configurable *config)
2523 if(config->random_file)
2524 free(config->random_file);
2525 if(config->egd_file)
2526 free(config->egd_file);
2528 free(config->userpwd);
2529 if(config->postfields)
2530 free(config->postfields);
2532 free(config->proxy);
2533 if(config->proxyuserpwd)
2534 free(config->proxyuserpwd);
2536 free(config->cookie);
2537 if(config->cookiefile)
2538 free(config->cookiefile);
2539 if(config->krb4level)
2540 free(config->krb4level);
2541 if(config->headerfile)
2542 free(config->headerfile);
2544 free(config->ftpport);
2546 free(config->range);
2547 if(config->customrequest)
2548 free(config->customrequest);
2549 if(config->writeout)
2550 free(config->writeout);
2551 if(config->httppost)
2552 curl_formfree(config->httppost);
2554 free(config->cacert);
2556 free(config->capath);
2557 if(config->cookiejar)
2558 free(config->cookiejar);
2560 curl_slist_free_all(config->quote); /* checks for config->quote == NULL */
2561 curl_slist_free_all(config->postquote); /* */
2562 curl_slist_free_all(config->headers); /* */
2565 #if defined(WIN32) && !defined(__CYGWIN32__)
2567 /* Function to find CACert bundle on a Win32 platform using SearchPath.
2568 * (SearchPath is defined in windows.h, which is #included into libcurl)
2569 * (Use the ASCII version instead of the unicode one!)
2570 * The order of the directories it searches is:
2571 * 1. application's directory
2572 * 2. current working directory
2573 * 3. Windows System directory (e.g. C:\windows\system32)
2574 * 4. Windows Directory (e.g. C:\windows)
2575 * 5. all directories along %PATH%
2577 static void FindWin32CACert(struct Configurable *config,
2578 const char *bundle_file)
2580 /* only check for cert file if "we" support SSL */
2581 if(curlinfo->features & CURL_VERSION_SSL) {
2584 char *retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
2588 buflen = SearchPathA(NULL, bundle_file, NULL, MAX_PATH+2, retval, &ptr);
2590 GetStr(&config->cacert, retval);
2599 operate(struct Configurable *config, int argc, char *argv[])
2601 char errorbuffer[CURL_ERROR_SIZE];
2602 char useragent[128]; /* buah, we don't want a larger default user agent */
2603 struct ProgressData progressbar;
2604 struct getout *urlnode;
2605 struct getout *nextnode;
2607 struct OutStruct outs;
2608 struct OutStruct heads;
2609 struct InStruct input;
2614 URLGlob *inglob=NULL;
2618 char *infiles; /* might a glob pattern */
2619 char *uploadfile=NULL; /* a single file, never a glob */
2625 FILE *headerfilep = NULL;
2626 char *urlbuffer=NULL;
2627 curl_off_t uploadfilesize; /* -1 means unknown */
2628 bool stillflags=TRUE;
2630 bool allocuseragent=FALSE;
2632 char *httpgetfields=NULL;
2637 int up; /* upload file counter within a single upload glob */
2641 /* this sends all memory debug messages to a logfile named memdump */
2642 env = curl_getenv("CURL_MEMDEBUG");
2645 curl_memdebug("memdump");
2647 env = curl_getenv("CURL_MEMLIMIT");
2649 curl_memlimit(atoi(env));
2654 memset(&outs,0,sizeof(outs));
2656 /* we get libcurl info right away */
2657 curlinfo = curl_version_info(CURLVERSION_NOW);
2659 errorbuffer[0]=0; /* prevent junk from being output */
2662 if (main_init() != CURLE_OK) {
2663 helpf("error initializing curl library\n");
2664 return CURLE_FAILED_INIT;
2666 config->showerror=TRUE;
2667 config->conf=CONF_DEFAULT;
2668 config->use_httpget=FALSE;
2669 config->create_dirs=FALSE;
2672 (!curl_strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
2673 strchr(argv[1], 'q')) {
2675 * The first flag, that is not a verbose name, but a shortname
2676 * and it includes the 'q' flag!
2681 parseconfig(NULL, config);
2684 if ((argc < 2) && !config->url_list) {
2686 return CURLE_FAILED_INIT;
2690 for (i = 1; i < argc; i++) {
2692 ('-' == argv[i][0])) {
2695 char *origopt=argv[i];
2697 char *flag = argv[i];
2699 if(curl_strequal("--", argv[i]))
2700 /* this indicates the end of the flags and thus enables the
2701 following (URL) argument to start with -. */
2704 nextarg= (i < argc - 1)? argv[i+1]: NULL;
2706 res = getparameter(flag, nextarg, &passarg, config);
2708 const char *reason = param2text(res);
2709 if(res != PARAM_HELP_REQUESTED)
2710 helpf("option %s: %s\n", origopt, reason);
2711 clean_getout(config);
2712 return CURLE_FAILED_INIT;
2715 if(passarg) /* we're supposed to skip this */
2721 /* just add the URL please */
2722 res = getparameter((char *)"--url", argv[i], &used, config);
2728 if(!config->url_list || !config->url_list->url) {
2729 clean_getout(config);
2730 helpf("no URL specified!\n");
2731 return CURLE_FAILED_INIT;
2733 if(NULL == config->useragent) {
2734 /* set non-zero default values: */
2735 snprintf(useragent, sizeof(useragent),
2736 CURL_NAME "/" CURL_VERSION " (" OS ") " "%s", curl_version());
2737 config->useragent= useragent;
2740 allocuseragent = TRUE;
2742 /* On WIN32 (non-cygwin), we can't set the path to curl-ca-bundle.crt
2743 * at compile time. So we look here for the file in two ways:
2744 * 1: look at the environment variable CURL_CA_BUNDLE for a path
2745 * 2: if #1 isn't found, use the windows API function SearchPath()
2746 * to find it along the app's path (includes app's dir and CWD)
2748 * We support the environment variable thing for non-Windows platforms
2749 * too. Just for the sake of it.
2751 if (!config->cacert &&
2753 !config->insecure_ok) {
2754 env = curl_getenv("CURL_CA_BUNDLE");
2756 GetStr(&config->cacert, env);
2759 #if defined(WIN32) && !defined(__CYGWIN32__)
2761 FindWin32CACert(config, "curl-ca-bundle.crt");
2765 if (config->postfields) {
2766 if (config->use_httpget) {
2767 /* Use the postfields data for a http get */
2768 httpgetfields = strdup(config->postfields);
2769 free(config->postfields);
2770 config->postfields = NULL;
2771 if(SetHTTPrequest((config->conf&CONF_NOBODY?HTTPREQ_HEAD:HTTPREQ_GET),
2772 &config->httpreq)) {
2773 free(httpgetfields);
2774 return PARAM_BAD_USE;
2778 if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
2779 return PARAM_BAD_USE;
2784 * Get a curl handle to use for all forthcoming curl transfers. Cleanup
2785 * when all transfers are done.
2787 curl = curl_easy_init();
2789 clean_getout(config);
2790 return CURLE_FAILED_INIT;
2793 /* After this point, we should call curl_easy_cleanup() if we decide to bail
2794 * out from this function! */
2796 urlnode = config->url_list;
2798 if(config->headerfile) {
2799 /* open file for output: */
2800 if(strcmp(config->headerfile,"-")) {
2801 heads.filename = config->headerfile;
2806 heads.stream = headerfilep;
2807 heads.config = config;
2810 /* loop through the list of given URLs */
2811 while(urlnode && !res) {
2814 /* get the full URL (it might be NULL) */
2820 /* This node had no URL, skip it and continue to the next */
2821 if(urlnode->outfile)
2822 free(urlnode->outfile);
2824 /* move on to the next URL */
2825 nextnode=urlnode->next;
2826 free(urlnode); /* free the node */
2828 continue; /* next please */
2831 /* default output stream is stdout */
2832 outs.stream = stdout;
2833 outs.config = config;
2835 /* save outfile pattern before expansion */
2836 outfiles = urlnode->outfile?strdup(urlnode->outfile):NULL;
2838 infiles = urlnode->infile;
2840 if(!config->globoff && infiles) {
2841 /* Unless explicitly shut off */
2842 res = glob_url(&inglob, infiles, &infilenum,
2844 (config->errors?config->errors:stderr):NULL);
2845 if(res != CURLE_OK) {
2846 clean_getout(config);
2851 /* Here's the loop for uploading multiple files within the same
2852 single globbed string. If no upload, we enter the loop once anyway. */
2854 (!up && !infiles) ||
2855 (uploadfile = inglob?
2856 glob_next_url(inglob):
2857 (!up?strdup(infiles):NULL));
2861 if(!config->globoff) {
2862 /* Unless explicitly shut off, we expand '{...}' and '[...]'
2863 expressions and return total number of URLs in pattern set */
2864 res = glob_url(&urls, dourl, &urlnum,
2866 (config->errors?config->errors:stderr):NULL);
2867 if(res != CURLE_OK) {
2872 urlnum = 1; /* without globbing, this is a single URL */
2874 /* if multiple files extracted to stdout, insert separators! */
2875 separator= ((!outfiles || curl_strequal(outfiles, "-")) && urlnum > 1);
2877 /* Here's looping around each globbed URL */
2879 (url = urls?glob_next_url(urls):(i?NULL:strdup(url)));
2882 outfile = outfiles?strdup(outfiles):NULL;
2884 if((urlnode->flags&GETOUT_USEREMOTE) ||
2885 (outfile && !curl_strequal("-", outfile)) ) {
2888 * We have specified a file name to store the result in, or we have
2889 * decided we want to use the remote file name.
2893 /* Find and get the remote file name */
2894 char * pc =strstr(url, "://");
2899 pc = strrchr(pc, '/');
2902 /* duplicate the string beyond the slash */
2904 outfile = *pc ? strdup(pc): NULL;
2906 if(!outfile || !*outfile) {
2907 helpf("Remote file name has no length!\n");
2908 res = CURLE_WRITE_ERROR;
2912 #if defined(__DJGPP__)
2914 /* This is for DOS, and then we do some major replacing of
2915 bad characters in the file name before using it */
2916 char *file1=xmalloc(PATH_MAX);
2917 strcpy(file1, msdosify(outfile));
2918 strcpy(outfile, rename_if_dos_device_name(file1));
2921 #endif /* __DJGPP__ */
2924 /* fill '#1' ... '#9' terms from URL pattern */
2925 char *storefile = outfile;
2926 outfile = glob_match_url(storefile, urls);
2930 fprintf(stderr, "bad output glob!\n");
2932 res = CURLE_FAILED_INIT;
2937 /* Create the directory hierarchy, if not pre-existant to a multiple
2940 if(config->create_dirs)
2941 if (-1 == create_dir_hierarchy(outfile)) {
2942 return CURLE_WRITE_ERROR;
2945 if(config->resume_from_current) {
2946 /* We're told to continue from where we are now. Get the
2947 size of the file as it is now and open it for append instead */
2949 struct stat fileinfo;
2951 /*VMS?? -- Danger, the filesize is only valid for stream files */
2952 if(0 == stat(outfile, &fileinfo))
2953 /* set offset to current file size: */
2954 config->resume_from = fileinfo.st_size;
2956 /* let offset be 0 */
2957 config->resume_from = 0;
2960 outs.filename = outfile;
2962 if(config->resume_from) {
2963 /* open file for output: */
2964 outs.stream=(FILE *) fopen(outfile, config->resume_from?"ab":"wb");
2966 helpf("Can't open '%s'!\n", outfile);
2967 return CURLE_WRITE_ERROR;
2971 outs.stream = NULL; /* open when needed */
2975 if(uploadfile && !curl_strequal(uploadfile, "-")) {
2977 * We have specified a file to upload and it isn't "-".
2979 struct stat fileinfo;
2981 /* If no file name part is given in the URL, we add this file name */
2982 char *ptr=strstr(url, "://");
2987 ptr = strrchr(ptr, '/');
2988 if(!ptr || !strlen(++ptr)) {
2989 /* The URL has no file name part, add the local file name. In order
2990 to be able to do so, we have to create a new URL in another
2993 /* We only want the part of the local path that is on the right
2994 side of the rightmost slash and backslash. */
2995 char *filep = strrchr(uploadfile, '/');
2996 char *file2 = strrchr(filep?filep:uploadfile, '\\');
3005 /* URL encode the file name */
3006 filep = curl_escape(filep, 0 /* use strlen */);
3010 urlbuffer=(char *)malloc(strlen(url) + strlen(filep) + 3);
3012 helpf("out of memory\n");
3013 return CURLE_OUT_OF_MEMORY;
3016 /* there is a trailing slash on the URL */
3017 sprintf(urlbuffer, "%s%s", url, filep);
3019 /* thers is no trailing slash on the URL */
3020 sprintf(urlbuffer, "%s/%s", url, filep);
3025 url = urlbuffer; /* use our new URL instead! */
3028 /*VMS??-- Reading binary from files can be a problem... */
3029 /*VMS?? Only FIXED, VAR etc WITHOUT implied CC will work */
3030 /*VMS?? Others need a \n appended to a line */
3031 /*VMS??-- Stat gives a size but this is UNRELIABLE in VMS */
3032 /*VMS?? As a f.e. a fixed file with implied CC needs to have a byte added */
3033 /*VMS?? for every record processed, this can by derived from Filesize & recordsize */
3034 /*VMS?? for VARiable record files the records need to be counted! */
3035 /*VMS?? for every record add 1 for linefeed and subtract 2 for the record header */
3036 /*VMS?? for VARIABLE header files only the bare record data needs to be considered with one appended if implied CC */
3038 infd=(FILE *) fopen(uploadfile, "rb");
3039 if (!infd || stat(uploadfile, &fileinfo)) {
3040 helpf("Can't open '%s'!\n", uploadfile);
3041 return CURLE_READ_ERROR;
3044 uploadfilesize=fileinfo.st_size;
3047 else if(uploadfile && curl_strequal(uploadfile, "-")) {
3051 if(uploadfile && config->resume_from_current)
3052 config->resume_from = -1; /* -1 will then force get-it-yourself */
3054 if(outs.stream && isatty(fileno(outs.stream)))
3055 /* we send the output to a tty, therefore we switch off the progress
3057 config->conf |= CONF_NOPROGRESS;
3059 if (urlnum > 1 && !(config->conf&CONF_MUTE)) {
3060 fprintf(stderr, "\n[%d/%d]: %s --> %s\n",
3061 i+1, urlnum, url, outfile ? outfile : "<stdout>");
3063 printf("%s%s\n", CURLseparator, url);
3065 if (httpgetfields) {
3066 /* Find out whether the url contains a file name */
3067 const char *pc =strstr(url, "://");
3074 pc = strrchr(pc, '/'); /* check for a slash */
3077 /* there is a slash present in the URL */
3080 /* Ouch, there's already a question mark in the URL string, we
3081 then append the data with an ampersand separator instead! */
3085 * Then append ? followed by the get fields to the url.
3087 urlbuffer=(char *)malloc(strlen(url) + strlen(httpgetfields) + 2);
3089 helpf("out of memory\n");
3090 return CURLE_OUT_OF_MEMORY;
3093 sprintf(urlbuffer, "%s%c%s", url, sep, httpgetfields);
3095 /* Append / before the ? to create a well-formed url
3096 if the url contains a hostname only
3098 sprintf(urlbuffer, "%s/?%s", url, httpgetfields);
3100 free(url); /* free previous URL */
3101 url = urlbuffer; /* use our new URL instead! */
3105 config->errors = stderr;
3108 if(!outfile && !(config->conf & CONF_GETTEXT)) {
3109 /* We get the output to stdout and we have not got the ASCII/text flag,
3110 then set stdout to be binary */
3111 setmode( fileno(stdout), O_BINARY );
3115 curl_easy_setopt(curl, CURLOPT_SSLENGINE, config->engine);
3116 curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1);
3118 /* where to store */
3119 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (FILE *)&outs);
3120 /* what call to write */
3121 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
3124 input.stream = infd;
3125 input.config = config;
3126 curl_easy_setopt(curl, CURLOPT_READDATA, &input);
3127 /* what call to read */
3128 curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_fread);
3130 if(config->recvpersecond) {
3131 /* tell libcurl to use a smaller sized buffer as it allows us to
3132 make better sleeps! 7.9.9 stuff! */
3133 curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond);
3136 /* size of uploaded file: */
3137 curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);
3138 curl_easy_setopt(curl, CURLOPT_URL, url); /* what to fetch */
3139 curl_easy_setopt(curl, CURLOPT_PROXY, config->proxy); /* proxy to use */
3140 curl_easy_setopt(curl, CURLOPT_HEADER, config->conf&CONF_HEADER);
3141 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, config->conf&CONF_NOPROGRESS);
3142 curl_easy_setopt(curl, CURLOPT_NOBODY, config->conf&CONF_NOBODY);
3143 curl_easy_setopt(curl, CURLOPT_FAILONERROR,
3144 config->conf&CONF_FAILONERROR);
3145 curl_easy_setopt(curl, CURLOPT_UPLOAD, uploadfile?TRUE:FALSE);
3146 curl_easy_setopt(curl, CURLOPT_FTPLISTONLY,
3147 config->conf&CONF_FTPLISTONLY);
3148 curl_easy_setopt(curl, CURLOPT_FTPAPPEND, config->conf&CONF_FTPAPPEND);
3150 if (config->conf&CONF_NETRC_OPT)
3151 curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
3152 else if (config->conf&CONF_NETRC)
3153 curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED);
3155 curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED);
3157 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,
3158 config->conf&CONF_FOLLOWLOCATION);
3159 curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH,
3160 config->conf&CONF_UNRESTRICTED_AUTH);
3161 curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, config->conf&CONF_GETTEXT);
3162 curl_easy_setopt(curl, CURLOPT_USERPWD, config->userpwd);
3163 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd);
3164 curl_easy_setopt(curl, CURLOPT_RANGE, config->range);
3165 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
3166 curl_easy_setopt(curl, CURLOPT_TIMEOUT, config->timeout);
3167 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config->postfields);
3169 /* new in libcurl 7.2: */
3170 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config->postfieldsize);
3172 curl_easy_setopt(curl, CURLOPT_REFERER, config->referer);
3173 curl_easy_setopt(curl, CURLOPT_AUTOREFERER,
3174 config->conf&CONF_AUTO_REFERER);
3175 curl_easy_setopt(curl, CURLOPT_USERAGENT, config->useragent);
3176 curl_easy_setopt(curl, CURLOPT_FTPPORT, config->ftpport);
3177 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, config->low_speed_limit);
3178 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time);
3179 curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE,
3180 config->use_resume?config->resume_from:0);
3181 curl_easy_setopt(curl, CURLOPT_COOKIE, config->cookie);
3182 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, config->headers);
3183 curl_easy_setopt(curl, CURLOPT_HTTPPOST, config->httppost);
3184 curl_easy_setopt(curl, CURLOPT_SSLCERT, config->cert);
3185 curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
3186 curl_easy_setopt(curl, CURLOPT_SSLKEY, config->key);
3187 curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, config->key_type);
3188 curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, config->key_passwd);
3190 /* default to strict verifyhost */
3191 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
3192 if(config->cacert || config->capath) {
3194 curl_easy_setopt(curl, CURLOPT_CAINFO, config->cacert);
3197 curl_easy_setopt(curl, CURLOPT_CAPATH, config->capath);
3198 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE);
3200 if(config->insecure_ok) {
3201 /* new stuff needed for libcurl 7.10 */
3202 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
3203 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
3206 if((config->conf&CONF_NOBODY) ||
3207 config->remote_time) {
3208 /* no body or use remote time */
3209 curl_easy_setopt(curl, CURLOPT_FILETIME, TRUE);
3212 if (config->maxredirs)
3213 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs);
3215 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, DEFAULT_MAXREDIRS);
3217 curl_easy_setopt(curl, CURLOPT_CRLF, config->crlf);
3218 curl_easy_setopt(curl, CURLOPT_QUOTE, config->quote);
3219 curl_easy_setopt(curl, CURLOPT_POSTQUOTE, config->postquote);
3220 curl_easy_setopt(curl, CURLOPT_WRITEHEADER,
3221 config->headerfile?&heads:NULL);
3222 curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config->cookiefile);
3223 /* cookie jar was added in 7.9 */
3224 if(config->cookiejar)
3225 curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config->cookiejar);
3226 /* cookie session added in 7.9.7 */
3227 curl_easy_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession);
3229 curl_easy_setopt(curl, CURLOPT_SSLVERSION, config->ssl_version);
3230 curl_easy_setopt(curl, CURLOPT_TIMECONDITION, config->timecond);
3231 curl_easy_setopt(curl, CURLOPT_TIMEVALUE, config->condtime);
3232 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
3233 curl_easy_setopt(curl, CURLOPT_STDERR, config->errors);
3235 /* three new ones in libcurl 7.3: */
3236 curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel);
3237 curl_easy_setopt(curl, CURLOPT_INTERFACE, config->iface);
3238 curl_easy_setopt(curl, CURLOPT_KRB4LEVEL, config->krb4level);
3240 progressbarinit(&progressbar, config);
3241 if((config->progressmode == CURL_PROGRESS_BAR) &&
3242 !(config->conf&(CONF_NOPROGRESS|CONF_MUTE))) {
3243 /* we want the alternative style, then we have to implement it
3245 curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress);
3246 curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
3249 /* new in libcurl 7.6.2: */
3250 curl_easy_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options);
3252 /* new in libcurl 7.7: */
3253 curl_easy_setopt(curl, CURLOPT_RANDOM_FILE, config->random_file);
3254 curl_easy_setopt(curl, CURLOPT_EGDSOCKET, config->egd_file);
3255 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config->connecttimeout);
3257 if(config->cipher_list)
3258 curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list);
3260 if(config->httpversion)
3261 curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config->httpversion);
3263 /* new in libcurl 7.9.2: */
3264 if(config->disable_epsv)
3266 curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE);
3268 /* new in libcurl 7.10.5 */
3269 if(config->disable_eprt)
3271 curl_easy_setopt(curl, CURLOPT_FTP_USE_EPRT, FALSE);
3273 /* new in libcurl 7.10.6 (default is Basic) */
3274 if(config->authtype)
3275 curl_easy_setopt(curl, CURLOPT_HTTPAUTH, config->authtype);
3277 /* new in curl 7.9.7 */
3278 if(config->trace_dump) {
3279 curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);
3280 curl_easy_setopt(curl, CURLOPT_DEBUGDATA, config);
3281 config->conf |= CONF_VERBOSE; /* force verbose */
3283 curl_easy_setopt(curl, CURLOPT_VERBOSE, config->conf&CONF_VERBOSE);
3285 /* new in curl 7.10 */
3286 curl_easy_setopt(curl, CURLOPT_ENCODING,
3287 (config->encoding) ? "" : NULL);
3289 /* new in curl 7.10.7 */
3290 curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS,
3291 config->ftp_create_dirs);
3292 if(config->proxyntlm)
3293 curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
3295 /* new in curl 7.10.8 */
3296 if(config->max_filesize)
3297 curl_easy_setopt(curl, CURLOPT_MAXFILESIZE_LARGE,
3298 config->max_filesize);
3300 /* new in curl 7.11.0 */
3302 curl_easy_setopt(curl, CURLOPT_FTP_SSL, CURLFTPSSL_TRY);
3304 /* new in curl 7.11.1 */
3305 if(config->socks5proxy) {
3306 curl_easy_setopt(curl, CURLOPT_PROXY, config->socks5proxy);
3307 curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
3310 res = curl_easy_perform(curl);
3312 if((config->progressmode == CURL_PROGRESS_BAR) &&
3313 progressbar.calls) {
3314 /* if the custom progress bar has been displayed, we output a
3316 fputs("\n", progressbar.out);
3319 if(config->writeout) {
3320 ourWriteOut(curl, config->writeout);
3322 #ifdef USE_ENVIRONMENT
3323 if (config->writeenv)
3328 if (!config->showerror) {
3329 vms_show = VMSSTS_HIDE;
3332 if((res!=CURLE_OK) && config->showerror) {
3333 if(CURLE_SSL_CACERT == res) {
3334 fprintf(config->errors, "curl: (%d) %s\n\n", res, errorbuffer);
3335 #define CURL_CA_CERT_ERRORMSG1 \
3336 "More details here: http://curl.haxx.se/docs/sslcerts.html\n\n" \
3337 "curl performs SSL certificate verification by default, using a \"bundle\"\n" \
3338 " of Certificate Authority (CA) public keys (CA certs). The default\n" \
3339 " bundle is named curl-ca-bundle.crt; you can specify an alternate file\n" \
3340 " using the --cacert option.\n"
3342 #define CURL_CA_CERT_ERRORMSG2 \
3343 "If this HTTPS server uses a certificate signed by a CA represented in\n" \
3344 " the bundle, the certificate verification probably failed due to a\n" \
3345 " problem with the certificate (it might be expired, or the name might\n" \
3346 " not match the domain name in the URL).\n" \
3347 "If you'd like to turn off curl's verification of the certificate, use\n" \
3348 " the -k (or --insecure) option.\n"
3350 fprintf(config->errors, "%s%s",
3351 CURL_CA_CERT_ERRORMSG1,
3352 CURL_CA_CERT_ERRORMSG2 );
3355 fprintf(config->errors, "curl: (%d) %s\n", res, errorbuffer);
3359 if (outfile && !curl_strequal(outfile, "-") && outs.stream)
3360 fclose(outs.stream);
3363 /* Important that we set the time _after_ the file has been
3364 closed, as is done above here */
3365 if(config->remote_time && outs.filename) {
3366 /* as libcurl if we got a time. Pretty please */
3368 curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
3370 struct utimbuf times;
3371 times.actime = filetime;
3372 times.modtime = filetime;
3373 utime(outs.filename, ×); /* set the time we got */
3378 /* Set the url as comment for the file. (up to 80 chars are allowed)
3380 if( strlen(url) > 78 )
3383 SetComment( outs.filename, url);
3387 fclose(headerfilep);
3390 free(httpgetfields);
3401 } /* loop to the next URL */
3404 /* cleanup memory used for URL globbing patterns */
3410 } /* loop to the next globbed upload file */
3413 glob_cleanup(inglob);
3418 /* empty this urlnode struct */
3421 if(urlnode->outfile)
3422 free(urlnode->outfile);
3424 free(urlnode->infile);
3426 /* move on to the next URL */
3427 nextnode=urlnode->next;
3428 free(urlnode); /* free the node */
3431 } /* while-loop through all URLs */
3433 if(config->headerfile && !headerfilep && heads.stream)
3434 fclose(heads.stream);
3436 if(config->trace_fopened && config->trace_stream)
3437 fclose(config->trace_stream);
3440 free(config->useragent);
3442 /* cleanup the curl handle! */
3443 curl_easy_cleanup(curl);
3445 if(config->errors_fopened)
3446 fclose(config->errors);
3448 main_free(); /* cleanup */
3453 int main(int argc, char *argv[])
3456 struct Configurable config;
3457 memset(&config, 0, sizeof(struct Configurable));
3459 res = operate(&config, argc, argv);
3460 free_config_fields(&config);
3462 #ifdef __NOVELL_LIBC__
3466 if (res > CURL_LAST) res = CURL_LAST; /* If CURL_LAST exceeded then */
3467 return (vms_cond[res]|vms_show); /* curlmsg.h is out of sync. */
3473 static char *my_get_line(FILE *fp)
3477 char *retval = NULL;
3480 if (NULL == fgets(buf, sizeof(buf), fp))
3483 retval = strdup(buf);
3485 if (NULL == (retval = realloc(retval,
3486 strlen(retval) + strlen(buf) + 1)))
3488 strcat(retval, buf);
3491 while (NULL == (nl = strchr(retval, '\n')));
3500 /* Create the needed directory hierarchy recursively in order to save
3501 multi-GETs in file output, ie:
3502 curl "http://my.site/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt"
3503 should create all the dir* automagically
3505 static int create_dir_hierarchy(char *outfile)
3513 outdup = strdup(outfile);
3514 dirbuildup = malloc(sizeof(char) * strlen(outfile));
3517 dirbuildup[0] = '\0';
3519 tempdir = strtok(outdup, DIR_CHAR);
3521 while (tempdir != NULL) {
3522 tempdir2 = strtok(NULL, DIR_CHAR);
3523 /* since strtok returns a token for the last word even
3524 if not ending with DIR_CHAR, we need to prune it */
3525 if (tempdir2 != NULL) {
3526 if (strlen(dirbuildup) > 0)
3527 sprintf(dirbuildup,"%s%s%s",dirbuildup, DIR_CHAR, tempdir);
3529 if (0 != strncmp(outdup, DIR_CHAR, 1))
3530 sprintf(dirbuildup,"%s",tempdir);
3532 sprintf(dirbuildup,"%s%s", DIR_CHAR, tempdir);
3534 if (access(dirbuildup, F_OK) == -1) {
3535 result = mkdir(dirbuildup,(mode_t)0000750);
3540 fprintf(stderr,"You don't have permission to create %s.\n",
3546 fprintf(stderr,"The directory name %s is too long.\n",
3552 fprintf(stderr,"%s resides on a read-only file system.\n",
3558 fprintf(stderr,"No space left on the file system that will "
3559 "contain the directory %s.\n", dirbuildup);
3564 fprintf(stderr,"Cannot create directory %s because you "
3565 "exceeded your quota.\n", dirbuildup);
3569 fprintf(stderr,"Error creating directory %s.\n", dirbuildup);
3572 break; /* get out of loop */
3581 return result; /* 0 is fine, -1 is badness */
3585 /* The following functions are taken with modification from the DJGPP
3586 * port of tar 1.12. They use algorithms originally from DJTAR. */
3589 msdosify (char *file_name)
3591 static char dos_name[PATH_MAX];
3592 static char illegal_chars_dos[] = ".+, ;=[]|<>\\\":?*";
3593 static char *illegal_chars_w95 = &illegal_chars_dos[8];
3595 char *s = file_name, *d = dos_name;
3596 char *illegal_aliens = illegal_chars_dos;
3597 size_t len = sizeof (illegal_chars_dos) - 1;
3600 /* Support for Windows 9X VFAT systems, when available. */
3601 if (_use_lfn (file_name))
3604 illegal_aliens = illegal_chars_w95;
3605 len -= (illegal_chars_w95 - illegal_chars_dos);
3608 /* Get past the drive letter, if any. */
3609 if (s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
3614 for (idx = 0, dot_idx = -1; *s; s++, d++) {
3615 if (memchr (illegal_aliens, *s, len)) {
3616 /* Dots are special: DOS doesn't allow them as the leading character,
3617 and a file name cannot have more than a single dot. We leave the
3618 first non-leading dot alone, unless it comes too close to the
3619 beginning of the name: we want sh.lex.c to become sh_lex.c, not
3622 if (idx == 0 && (s[1] == '/' || (s[1] == '.' && s[2] == '/'))) {
3623 /* Copy "./" and "../" verbatim. */
3631 else if (dot_idx >= 0) {
3632 if (dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */
3633 d[dot_idx - idx] = '_'; /* replace previous dot */
3645 else if (*s == '+' && s[1] == '+') {
3646 if (idx - 2 == dot_idx) { /* .c++, .h++ etc. */
3652 memcpy (d, "plus", 4);
3676 rename_if_dos_device_name (char *file_name)
3678 /* We could have a file whose name is a device on MS-DOS. Trying to
3679 * retrieve such a file would fail at best and wedge us at worst. We need
3680 * to rename such files. */
3681 extern char *basename (const char *);
3684 char fname[PATH_MAX];
3686 strcpy (fname, file_name);
3687 base = basename (fname);
3688 if (((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
3689 size_t blen = strlen (base);
3691 /* Prepend a '_'. */
3692 memmove (base + 1, base, blen + 1);
3694 strcpy (file_name, fname);
3699 #endif /* __DJGPP__ */