1 /*****************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * The contents of this file are subject to the Mozilla Public License
9 * Version 1.0 (the "License"); you may not use this file except in
10 * compliance with the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
13 * Software distributed under the License is distributed on an "AS IS"
14 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
15 * License for the specific language governing rights and limitations
18 * The Original Code is Curl.
20 * The Initial Developer of the Original Code is Daniel Stenberg.
22 * Portions created by the Initial Developer are Copyright (C) 1998.
23 * All Rights Reserved.
25 * ------------------------------------------------------------
27 * - Daniel Stenberg <daniel@haxx.se>
38 * ------------------------------------------------------------
39 ****************************************************************************/
48 #include <curl/curl.h>
49 #include <curl/types.h> /* new for v7 */
50 #include <curl/easy.h> /* new for v7 */
52 #define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
53 #include <curl/mprintf.h>
58 #define CURLseparator "--_curl_--"
59 #define MIMEseparator "_curl_"
61 /* This define make use of the "Curlseparator" as opposed to the
62 MIMEseparator. We might add support for the latter one in the
63 future, and that's why this is left in the source. */
64 #define CURL_SEPARATORS
66 /* This is now designed to have its own local setup.h */
75 #ifdef HAVE_IO_H /* typical win32 habit */
87 /* The last #include file should be: */
89 /* this is low-level hard-hacking memory leak tracking shit */
90 #include "../lib/memdebug.h"
93 #define DEFAULT_MAXREDIRS 50L
95 #ifndef __cplusplus /* (rabe) */
99 #define CURL_PROGRESS_STATS 0 /* default progress display */
100 #define CURL_PROGRESS_BAR 1
112 /* Just a set of bits */
113 #define CONF_DEFAULT 0
115 #define CONF_USEREMOTETIME (1<<0) /* set the remote time on the local file */
116 #define CONF_AUTO_REFERER (1<<4) /* the automatic referer-system please! */
117 #define CONF_VERBOSE (1<<5) /* talk a lot */
118 #define CONF_HEADER (1<<8) /* throw the header out too */
119 #define CONF_NOPROGRESS (1<<10) /* shut off the progress meter */
120 #define CONF_NOBODY (1<<11) /* use HEAD to get http document */
121 #define CONF_FAILONERROR (1<<12) /* no output on http error codes >= 300 */
122 #define CONF_UPLOAD (1<<14) /* this is an upload */
123 #define CONF_POST (1<<15) /* HTTP POST method */
124 #define CONF_FTPLISTONLY (1<<16) /* Use NLST when listing ftp dir */
125 #define CONF_FTPAPPEND (1<<20) /* Append instead of overwrite on upload! */
126 #define CONF_NETRC (1<<22) /* read user+password from .netrc */
127 #define CONF_FOLLOWLOCATION (1<<23) /* use Location: Luke! */
128 #define CONF_GETTEXT (1<<24) /* use ASCII/text for transfer */
129 #define CONF_HTTPPOST (1<<25) /* multipart/form-data HTTP POST */
130 #define CONF_PUT (1<<27) /* PUT the input file */
131 #define CONF_MUTE (1<<28) /* force NOPROGRESS */
134 /* Ultrix doesn't have strdup(), so make a quick clone: */
135 char *strdup(char *str)
141 newstr = (char *) malloc((len+1)*sizeof(char));
152 extern void hugehelp(void);
154 /***********************************************************************
155 * Start with some silly functions to make win32-systems survive
156 ***********************************************************************/
157 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
158 static void win32_cleanup(void)
163 static CURLcode win32_init(void)
165 WORD wVersionRequested;
168 wVersionRequested = MAKEWORD(1, 1);
170 err = WSAStartup(wVersionRequested, &wsaData);
173 /* Tell the user that we couldn't find a useable */
175 return CURLE_FAILED_INIT;
177 /* Confirm that the Windows Sockets DLL supports 1.1.*/
178 /* Note that if the DLL supports versions greater */
179 /* than 1.1 in addition to 1.1, it will still return */
180 /* 1.1 in wVersion since that is the version we */
183 if ( LOBYTE( wsaData.wVersion ) != 1 ||
184 HIBYTE( wsaData.wVersion ) != 1 ) {
185 /* Tell the user that we couldn't find a useable */
189 return CURLE_FAILED_INIT;
193 /* The Windows Sockets DLL is acceptable. Proceed. */
195 static CURLcode win32_init(void) { return CURLE_OK; }
196 #define win32_cleanup()
201 * This is the main global constructor for the app. Call this before
202 * _any_ libcurl usage. If this fails, *NO* libcurl functions may be
203 * used, or havoc may be the result.
205 CURLcode main_init(void)
211 * This is the main global destructor for the app. Call this after
212 * _all_ libcurl usage is done.
219 int SetHTTPrequest(HttpReq req, HttpReq *store)
221 if((*store == HTTPREQ_UNSPEC) ||
226 fprintf(stderr, "You can only select one HTTP request!\n");
230 static void helpf(char *fmt, ...)
235 fputs("curl: ", stderr); /* prefix it */
236 vfprintf(stderr, fmt, ap);
239 fprintf(stderr, "curl: try 'curl --help' for more information\n");
242 static void help(void)
244 printf(CURL_ID "%s\n"
245 "Usage: curl [options...] <url>\n"
246 "Options: (H) means HTTP/HTTPS only, (F) means FTP only\n"
247 " -a/--append Append to target file when uploading (F)\n"
248 " -A/--user-agent <string> User-Agent to send to server (H)\n"
249 " -b/--cookie <name=string/file> Cookie string or file to read cookies from (H)\n"
250 " -B/--use-ascii Use ASCII/text transfer\n"
251 " -c/--continue Resume a previous transfer where we left it\n"
252 " -C/--continue-at <offset> Specify absolute resume offset\n"
253 " -d/--data <data> HTTP POST data (H)\n"
254 " --data-ascii <data> HTTP POST ASCII data (H)\n"
255 " --data-binary <data> HTTP POST binary data (H)\n"
256 " -D/--dump-header <file> Write the headers to this file\n"
257 " -e/--referer Referer page (H)\n"
258 " -E/--cert <cert[:passwd]> Specifies your certificate file and password (HTTPS)\n"
259 " --cacert <file> CA certifciate to verify peer against (HTTPS)\n"
260 " -f/--fail Fail silently (no output at all) on errors (H)\n"
261 " -F/--form <name=content> Specify HTTP POST data (H)\n"
263 " -h/--help This help text\n"
264 " -H/--header <line> Custom header to pass to server. (H)\n"
265 " -i/--include Include the HTTP-header in the output (H)\n"
266 " -I/--head Fetch document info only (HTTP HEAD/FTP SIZE)\n"
267 " --interface <interface> Specify the interface to be used\n"
268 " --krb4 <level> Enable krb4 with specified security level (F)\n"
269 " -K/--config Specify which config file to read\n"
270 " -l/--list-only List only names of an FTP directory (F)\n"
271 " -L/--location Follow Location: hints (H)\n"
272 " -m/--max-time <seconds> Maximum time allowed for the transfer\n"
273 " -M/--manual Display huge help text\n"
274 " -n/--netrc Read .netrc for user name and password\n"
275 " -N/--no-buffer Disables the buffering of the output stream\n"
276 " -o/--output <file> Write output to <file> instead of stdout\n"
277 " -O/--remote-name Write output to a file named as the remote file\n"
278 " -p/--proxytunnel Perform non-HTTP services through a HTTP proxy\n"
279 " -P/--ftpport <address> Use PORT with address instead of PASV when ftping (F)\n"
280 " -q When used as the first parameter disables .curlrc\n"
281 " -Q/--quote <cmd> Send QUOTE command to FTP before file transfer (F)\n"
282 " -r/--range <range> Retrieve a byte range from a HTTP/1.1 or FTP server\n"
283 " -s/--silent Silent mode. Don't output anything\n"
284 " -S/--show-error Show error. With -s, make curl show errors when they occur\n"
285 " -t/--upload Transfer/upload stdin to remote site\n"
286 " -T/--upload-file <file> Transfer/upload <file> to remote site\n"
287 " -u/--user <user[:password]> Specify user and password to use\n"
288 " -U/--proxy-user <user[:password]> Specify Proxy authentication\n"
289 " -v/--verbose Makes the operation more talkative\n"
290 " -V/--version Outputs version number then quits\n"
291 " -w/--write-out [format] What to output after completion\n"
292 " -x/--proxy <host[:port]> Use proxy. (Default port is 1080)\n"
293 " -X/--request <command> Specific request command to use\n"
294 " -y/--speed-time Time needed to trig speed-limit abort. Defaults to 30\n"
295 " -Y/--speed-limit Stop transfer if below speed-limit for 'speed-time' secs\n"
296 " -z/--time-cond <time> Includes a time condition to the server (H)\n"
297 " -Z/--max-redirs <num> Set maximum number of redirections allowed (H)\n"
298 " -2/--sslv2 Force usage of SSLv2 (H)\n"
299 " -3/--sslv3 Force usage of SSLv3 (H)\n"
300 " -#/--progress-bar Display transfer progress as a progress bar\n"
301 " --crlf Convert LF to CRLF in upload. Useful for MVS (OS/390)\n"
302 " --stderr <file> Where to redirect stderr. - means stdout.\n",
313 struct Configurable {
328 unsigned short porttouse;
351 char *writeout; /* %-styled format string to output */
353 FILE *errors; /* if stderr redirect is requested */
355 struct curl_slist *quote;
356 struct curl_slist *postquote;
362 struct curl_slist *headers;
364 struct HttpPost *httppost;
365 struct HttpPost *last_post;
370 static int parseconfig(char *filename,
371 struct Configurable *config);
372 static char *my_get_line(FILE *fp);
374 static void GetStr(char **string,
380 *string = strdup(value);
385 static char *file2string(FILE *file)
394 while(fgets(buffer, sizeof(buffer), file)) {
395 ptr= strchr(buffer, '\r');
398 ptr= strchr(buffer, '\n');
401 stringlen=strlen(buffer);
403 string = realloc(string, len+stringlen+1);
405 string = malloc(stringlen+1);
407 strcpy(string+len, buffer);
414 return NULL; /* no string */
417 static char *file2memory(FILE *file, long *size)
421 char *newstring=NULL;
426 while((len = fread(buffer, 1, sizeof(buffer), file))) {
428 newstring = realloc(string, len+stringlen);
432 break; /* no more strings attached! :-) */
435 string = malloc(len);
436 memcpy(&string[stringlen], buffer, len);
443 return NULL; /* no string */
448 PARAM_OPTION_AMBIGUOUS,
449 PARAM_OPTION_UNKNOWN,
450 PARAM_REQUIRES_PARAMETER,
452 PARAM_HELP_REQUESTED,
453 PARAM_GOT_EXTRA_PARAMETER,
458 static ParameterError getparameter(char *flag, /* f or -long-flag */
459 char *nextarg, /* NULL if unset */
460 bool *usedarg, /* set to TRUE if the arg
462 struct Configurable *config)
465 char subletter=0; /* subletters can only occur on long options */
476 boolean whether it takes an additional argument
478 struct LongShort aliases[]= {
479 {"9", "crlf", FALSE},
480 {"8", "stderr", TRUE},
481 {"7", "interface", TRUE},
485 {"2", "sslv2", FALSE},
486 {"3", "sslv3", FALSE},
487 {"a", "append", FALSE},
488 {"A", "user-agent", TRUE},
489 {"b", "cookie", TRUE},
490 {"B", "ftp-ascii", FALSE}, /* this long format is OBSOLETEE now! */
491 {"B", "use-ascii", FALSE},
492 {"c", "continue", FALSE},
493 {"C", "continue-at", TRUE},
495 {"da", "data-ascii", TRUE},
496 {"db", "data-binary", TRUE},
497 {"D", "dump-header", TRUE},
498 {"e", "referer", TRUE},
500 {"Ea", "cacert", TRUE},
501 {"f", "fail", FALSE},
504 {"h", "help", FALSE},
505 {"H", "header", TRUE},
506 {"i", "include", FALSE},
507 {"I", "head", FALSE},
508 {"K", "config", TRUE},
509 {"l", "list-only", FALSE},
510 {"L", "location", FALSE},
511 {"m", "max-time", TRUE},
512 {"M", "manual", FALSE},
513 {"n", "netrc", FALSE},
514 {"N", "no-buffer", FALSE},
515 {"o", "output", TRUE},
516 {"O", "remote-name", FALSE},
517 {"p", "proxytunnel", FALSE},
518 {"P", "ftpport", TRUE},
519 {"q", "disable", FALSE},
520 {"Q", "quote", TRUE},
521 {"r", "range", TRUE},
522 {"s", "silent", FALSE},
523 {"S", "show-error", FALSE},
524 {"t", "upload", FALSE},
525 {"T", "upload-file", TRUE},
527 {"U", "proxy-user", TRUE},
528 {"v", "verbose", FALSE},
529 {"V", "version", FALSE},
530 {"w", "write-out", TRUE},
531 {"x", "proxy", TRUE},
532 {"X", "request", TRUE},
533 {"X", "http-request", TRUE}, /* OBSOLETE VERSION */
534 {"Y", "speed-limit", TRUE},
535 {"y", "speed-time", TRUE},
536 {"z", "time-cond", TRUE},
537 {"Z", "max-redirs", TRUE},
538 {"#", "progress-bar",FALSE},
541 if(('-' != flag[0]) ||
542 (('-' == flag[0]) && ('-' == flag[1]))) {
543 /* this should be a long name */
544 char *word=('-' == flag[0])?flag+2:flag;
545 int fnam=strlen(word);
547 for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
548 if(strnequal(aliases[j].lname, word, fnam)) {
551 if(strequal(aliases[j].lname, word)) {
552 parse = aliases[j].letter;
554 numhits = 1; /* a single unique hit */
557 parse = aliases[j].letter;
562 /* this is at least the second match! */
563 return PARAM_OPTION_AMBIGUOUS;
566 return PARAM_OPTION_UNKNOWN;
570 flag++; /* prefixed with one dash, pass it */
576 /* we can loop here if we have multiple single-letters */
579 letter = parse?*parse:'\0';
582 subletter = parse[1];
584 *usedarg = FALSE; /* default is that we don't use the arg */
587 fprintf(stderr, "OPTION: %c %s\n", letter, nextarg?nextarg:"<null>");
590 for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
591 if(letter == aliases[j].letter[0]) {
597 return PARAM_OPTION_UNKNOWN;
601 return PARAM_OPTION_UNKNOWN;
603 if((!nextarg || !*nextarg) && aliases[hit].extraparam) {
604 return PARAM_REQUIRES_PARAMETER;
606 else if(nextarg && aliases[hit].extraparam)
607 *usedarg = TRUE; /* mark it as used */
610 case '9': /* there is no short letter for this */
611 /* LF -> CRLF conversinon? */
614 case '8': /* there is no short letter for this */
615 if(strcmp(nextarg, "-"))
616 config->errors = fopen(nextarg, "wt");
618 config->errors = stdout;
620 case '7': /* there is no short letter for this */
622 GetStr(&config->iface, nextarg);
624 case '6': /* there is no short letter for this */
625 /* krb4 level string */
626 GetStr(&config->krb4level, nextarg);
630 GetStr(&config->url, nextarg);
632 case '#': /* added 19990617 larsa */
633 config->progressmode ^= CURL_PROGRESS_BAR;
637 config->ssl_version = 2;
641 config->ssl_version = 3;
644 /* This makes the FTP sessions use APPE instead of STOR */
645 config->conf ^= CONF_FTPAPPEND;
648 /* This specifies the User-Agent name */
649 GetStr(&config->useragent, nextarg);
651 case 'b': /* cookie string coming up: */
652 if(nextarg[0] == '@') {
655 else if(strchr(nextarg, '=')) {
656 /* A cookie string must have a =-letter */
657 GetStr(&config->cookie, nextarg);
660 /* We have a cookie file to read from! */
661 GetStr(&config->cookiefile, nextarg);
664 /* use ASCII/text when transfering */
665 config->conf ^= CONF_GETTEXT;
668 /* This makes us continue an ftp transfer */
669 config->use_resume^=TRUE;
670 fprintf(stderr, "-c is a deprecated switch, use '-C -' instead!\n");
673 /* This makes us continue an ftp transfer at given position */
674 if(!strequal(nextarg, "-"))
675 config->resume_from= atoi(nextarg);
676 config->use_resume=TRUE;
683 if('@' == *nextarg) {
684 /* the data begins with a '@' letter, it means that a file name
685 or - (stdin) follows */
688 nextarg++; /* pass the @ */
690 if(strequal("-", nextarg))
693 file = fopen(nextarg, "r");
695 if(subletter == 'b') /* forced binary */
696 postdata = file2memory(file, &config->postfieldsize);
698 postdata = file2string(file);
699 if(file && (file != stdin))
703 GetStr(&postdata, nextarg);
706 if(config->postfields && *config->postfields) {
707 /* we already have a string, we append this one
708 with a separating &-letter */
709 char *oldpost=config->postfields;
710 config->postfields=maprintf("%s&%s", oldpost, postdata);
715 config->postfields=postdata;
717 if(config->postfields)
718 config->conf |= CONF_POST;
719 if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
720 return PARAM_BAD_USE;
723 /* dump-header to given file name */
724 GetStr(&config->headerfile, nextarg);
728 char *ptr = strstr(nextarg, ";auto");
730 /* Automatic referer requested, this may be combined with a
732 config->conf |= CONF_AUTO_REFERER;
733 *ptr = 0; /* zero terminate here */
735 GetStr(&config->referer, nextarg);
739 if(subletter == 'a') {
740 /* CA info PEM file */
741 GetStr(&config->cacert, nextarg);
744 char *ptr = strchr(nextarg, ':');
746 /* we have a password too */
749 GetStr(&config->cert_passwd, ptr);
751 GetStr(&config->cert, nextarg);
755 /* fail hard on errors */
756 config->conf ^= CONF_FAILONERROR;
759 /* "form data" simulation, this is a little advanced so lets do our best
760 to sort this out slowly and carefully */
761 if(curl_formparse(nextarg,
764 return PARAM_BAD_USE;
765 if(SetHTTPrequest(HTTPREQ_POST, &config->httpreq))
766 return PARAM_BAD_USE;
769 case 'h': /* h for help */
771 return PARAM_HELP_REQUESTED;
773 /* A custom header to append to a list */
774 config->headers = curl_slist_append(config->headers, nextarg);
777 config->conf ^= CONF_HEADER; /* include the HTTP header as well */
780 config->conf ^= CONF_HEADER; /* include the HTTP header in the output */
781 config->conf ^= CONF_NOBODY; /* don't fetch the body at all */
782 if(SetHTTPrequest(HTTPREQ_HEAD, &config->httpreq))
783 return PARAM_BAD_USE;
786 res = parseconfig(nextarg, config);
787 config->configread = TRUE;
792 config->conf ^= CONF_FTPLISTONLY; /* only list the names of the FTP dir */
795 config->conf ^= CONF_FOLLOWLOCATION; /* Follow Location: HTTP headers */
798 /* specified max time */
799 config->timeout = atoi(nextarg);
801 case 'M': /* M for manual, huge help */
803 return PARAM_HELP_REQUESTED;
805 /* pick info from .netrc, if this is used for http, curl will
806 automatically enfore user+password with the request */
807 config->conf ^= CONF_NETRC;
810 /* disable the output I/O buffering */
811 config->nobuffer ^= 1;
815 GetStr(&config->outfile, nextarg); /* write to this file */
819 config->remotefile ^= TRUE;
822 /* This makes the FTP sessions use PORT instead of PASV */
823 /* use <eth0> or <192.168.10.10> style addresses. Anything except
824 this will make us try to get the "default" address.
825 NOTE: this is a changed behaviour since the released 4.1!
827 GetStr(&config->ftpport, nextarg);
830 /* proxy tunnel for non-http protocols */
831 config->proxytunnel ^= TRUE;
834 case 'q': /* if used first, already taken care of, we do it like
835 this so we don't cause an error! */
838 /* QUOTE command to send to FTP server */
839 if(nextarg[0] == '-') {
840 /* prefixed with a dash makes it a POST TRANSFER one */
842 config->postquote = curl_slist_append(config->postquote, nextarg);
845 config->quote = curl_slist_append(config->quote, nextarg);
849 /* byte range requested */
850 GetStr(&config->range, nextarg);
853 /* don't show progress meter, don't show errors : */
854 config->conf |= (CONF_MUTE|CONF_NOPROGRESS);
855 config->showerror ^= TRUE; /* toggle off */
859 config->showerror ^= TRUE; /* toggle on if used with -s */
862 /* we are uploading */
863 config->conf ^= CONF_UPLOAD;
864 fprintf(stderr, "-t is a deprecated switch, use '-T -' instead!\n");
867 /* we are uploading */
868 config->conf |= CONF_UPLOAD;
869 if(!strequal("-", nextarg))
870 /* make - equal stdin */
871 GetStr(&config->infile, nextarg);
875 GetStr(&config->userpwd, nextarg);
878 /* Proxy user:password */
879 GetStr(&config->proxyuserpwd, nextarg);
882 config->conf ^= CONF_VERBOSE; /* talk a lot */
885 printf(CURL_ID "%s\n", curl_version());
886 return PARAM_HELP_REQUESTED;
888 /* get the output string */
889 if('@' == *nextarg) {
890 /* the data begins with a '@' letter, it means that a file name
891 or - (stdin) follows */
893 nextarg++; /* pass the @ */
894 if(strequal("-", nextarg))
897 file = fopen(nextarg, "r");
898 config->writeout = file2string(file);
899 if(file && (file != stdin))
903 GetStr(&config->writeout, nextarg);
907 GetStr(&config->proxy, nextarg);
911 GetStr(&config->customrequest, nextarg);
912 if(SetHTTPrequest(HTTPREQ_CUSTOM, &config->httpreq))
913 return PARAM_BAD_USE;
917 config->low_speed_time = atoi(nextarg);
918 if(!config->low_speed_limit)
919 config->low_speed_limit = 1;
922 /* low speed limit */
923 config->low_speed_limit = atoi(nextarg);
924 if(!config->low_speed_time)
925 config->low_speed_time=30;
927 case 'z': /* time condition coming up */
932 /* If-Modified-Since: (section 14.28 in RFC2068) */
933 config->timecond = TIMECOND_IFMODSINCE;
936 /* If-Unmodified-Since: (section 14.24 in RFC2068) */
937 config->timecond = TIMECOND_IFUNMODSINCE;
941 /* Last-Modified: (section 14.29 in RFC2068) */
942 config->timecond = TIMECOND_LASTMOD;
947 config->condtime=curl_getdate(nextarg, &now);
948 if(-1 == config->condtime) {
949 /* now let's see if it is a file name to get the time from instead! */
951 if(-1 == stat(nextarg, &statbuf)) {
952 /* failed, remove time condition */
953 config->timecond = TIMECOND_NONE;
956 /* pull the time out from the file */
957 config->condtime = statbuf.st_mtime;
962 /* specified max no of redirects (http(s)) */
963 config->maxredirs = atoi(nextarg);
966 default: /* unknown flag */
967 return PARAM_OPTION_UNKNOWN;
971 } while(*++parse && !*usedarg);
977 static int parseconfig(char *filename,
978 struct Configurable *config)
982 char filebuffer[256];
986 if(!filename || !*filename) {
987 /* NULL or no file name attempts to load .curlrc from the homedir! */
989 #define CURLRC DOT_CHAR "curlrc"
991 home = curl_getenv("HOME"); /* portable environment reader */
995 if(strlen(home)>(sizeof(filebuffer)-strlen(CURLRC))) {
1000 sprintf(filebuffer, "%s%s%s", home, DIR_CHAR, CURLRC);
1002 filename = filebuffer;
1005 if(strcmp(filename,"-"))
1006 file = fopen(filename, "r");
1018 #define isseparator(x) (((x)=='=') || ((x) == ':'))
1020 while (NULL != (aline = my_get_line(file))) {
1023 alloced_param=FALSE;
1025 /* lines with # in the fist column is a comment! */
1026 while(isspace((int)*line))
1040 /* the option keywords starts here */
1042 while(*line && !isspace((int)*line) && !isseparator(*line))
1044 /* ... and has ended here */
1046 *line++=0; /* zero terminate, we have a local copy of the data */
1049 fprintf(stderr, "GOT: %s\n", option);
1052 /* pass spaces and separator(s) */
1053 while(isspace((int)*line) || isseparator(*line))
1056 /* the parameter starts here (unless quoted) */
1059 /* quoted parameter, do the qoute dance */
1061 param=strdup(line); /* parameter */
1065 while(*line && (*line != '\"')) {
1070 /* default is to output the letter after the backslah */
1071 switch(out = *line) {
1073 continue; /* this'll break out of the loop */
1093 *ptr=0; /* always zero terminate */
1097 param=line; /* parameter starts here */
1098 while(*line && !isspace((int)*line))
1100 *line=0; /* zero terminate */
1103 fprintf(stderr, "PARAM: \"%s\"\n", param);
1105 res = getparameter(option, param, &usedarg, config);
1107 if(*param && !usedarg)
1108 /* we passed in a parameter that wasn't used! */
1109 res = PARAM_GOT_EXTRA_PARAMETER;
1111 if(res != PARAM_OK) {
1112 /* the help request isn't really an error */
1113 if(!strcmp(filename, "-")) {
1116 if(PARAM_HELP_REQUESTED != res) {
1120 case PARAM_GOT_EXTRA_PARAMETER:
1121 reason = "had unsupported trailing garbage";
1123 case PARAM_OPTION_UNKNOWN:
1124 reason = "is unknown";
1126 case PARAM_OPTION_AMBIGUOUS:
1127 reason = "is ambiguous";
1129 case PARAM_REQUIRES_PARAMETER:
1130 reason = "requires parameter";
1133 reason = "is badly used here";
1136 fprintf(stderr, "%s:%d: warning: '%s' %s\n",
1137 filename, lineno, option, reason);
1157 struct Configurable *config;
1160 int my_fwrite(void *buffer, size_t size, size_t nmemb, FILE *stream)
1162 struct OutStruct *out=(struct OutStruct *)stream;
1163 if(out && !out->stream) {
1164 /* open file for writing */
1165 out->stream=fopen(out->filename, "wb");
1167 return -1; /* failure */
1168 if(out->config->nobuffer) {
1169 /* disable output buffering */
1171 setvbuf(out->stream, NULL, _IONBF, 0);
1175 return fwrite(buffer, size, nmemb, out->stream);
1178 struct ProgressData {
1185 int myprogress (void *clientp,
1191 /* The original progress-bar source code was written for curl by Lars Aas,
1192 and this new edition inherites some of his concepts. */
1203 struct ProgressData *bar = (struct ProgressData *)clientp;
1204 size_t total = dltotal + ultotal;
1206 bar->point = dlnow + ulnow; /* we've come this far */
1209 int prevblock = bar->prev / 1024;
1210 int thisblock = bar->point / 1024;
1211 while ( thisblock > prevblock ) {
1212 fprintf( stderr, "#" );
1217 frac = (float) bar->point / (float) total;
1218 percent = frac * 100.0f;
1219 barwidth = bar->width - 7;
1220 num = (int) (((float)barwidth) * frac);
1222 for ( i = 0; i < num; i++ ) {
1226 sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
1227 sprintf( outline, format, line, percent );
1228 fprintf( stderr, "\r%s", outline );
1230 bar->prev = bar->point;
1235 void progressbarinit(struct ProgressData *bar)
1243 memset(bar, 0, sizeof(struct ProgressData));
1245 /* TODO: get terminal width through ansi escapes or something similar.
1246 try to update width when xterm is resized... - 19990617 larsa */
1249 * OS/2 users most likely won't have this env var set, and besides that
1250 * we're using our own way to determine screen width */
1251 colp = curl_getenv("COLUMNS");
1253 bar->width = atoi(colp);
1260 * We use this emx library call to get the screen width, and subtract
1261 * one from what we got in order to avoid a problem with the cursor
1262 * advancing to the next line if we print a string that is as long as
1263 * the screen is wide. */
1266 bar->width = scr_size[0] - 1;
1271 void free_config_fields(struct Configurable *config)
1276 free(config->userpwd);
1277 if(config->postfields)
1278 free(config->postfields);
1280 free(config->proxy);
1281 if(config->proxyuserpwd)
1282 free(config->proxyuserpwd);
1284 free(config->cookie);
1285 if(config->cookiefile)
1286 free(config->cookiefile);
1287 if(config->krb4level)
1288 free(config->krb4level);
1289 if(config->headerfile)
1290 free(config->headerfile);
1292 free(config->outfile);
1294 free(config->ftpport);
1296 free(config->infile);
1298 free(config->range);
1299 if(config->customrequest)
1300 free(config->customrequest);
1301 if(config->writeout)
1302 free(config->writeout);
1303 if(config->httppost)
1304 curl_formfree(config->httppost);
1306 free(config->cacert);
1308 curl_slist_free_all(config->quote); /* checks for config->quote == NULL */
1309 curl_slist_free_all(config->postquote); /* */
1310 curl_slist_free_all(config->headers); /* */
1315 operate(struct Configurable *config, int argc, char *argv[])
1317 char errorbuffer[CURL_ERROR_SIZE];
1318 char useragent[128]; /* buah, we don't want a larger default user agent */
1319 struct ProgressData progressbar;
1321 struct OutStruct outs;
1322 struct OutStruct heads;
1332 FILE *headerfilep = NULL;
1333 char *urlbuffer=NULL;
1334 int infilesize=-1; /* -1 means unknown */
1335 bool stillflags=TRUE;
1337 bool allocuseragent=FALSE;
1343 outs.stream = stdout;
1344 outs.config = config;
1347 /* this sends all memory debug messages to a logfile named memdump */
1348 curl_memdebug("memdump");
1351 config->showerror=TRUE;
1352 config->conf=CONF_DEFAULT;
1359 (!strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
1360 strchr(argv[1], 'q')) {
1362 * The first flag, that is not a verbose name, but a shortname
1363 * and it includes the 'q' flag!
1366 fprintf(stderr, "I TURNED OFF THE CRAP\n");
1371 res = parseconfig(NULL, config);
1376 if ((argc < 2) && !config->url) {
1378 return CURLE_FAILED_INIT;
1382 for (i = 1; i < argc; i++) {
1384 ('-' == argv[i][0])) {
1387 char *origopt=argv[i];
1389 char *flag = argv[i];
1391 if(strequal("--", argv[i]))
1392 /* this indicates the end of the flags and thus enables the
1393 following (URL) argument to start with -. */
1396 nextarg= (i < argc - 1)? argv[i+1]: NULL;
1398 res = getparameter(flag, nextarg, &passarg, config);
1401 case PARAM_OPTION_AMBIGUOUS:
1402 helpf("option %s is ambiguous\n", origopt);
1404 case PARAM_OPTION_UNKNOWN:
1405 helpf("option %s is unknown\n", origopt);
1407 case PARAM_REQUIRES_PARAMETER:
1408 helpf("option %s requires an extra argument!\n", origopt);
1411 helpf("option %s was wrongly used!\n", origopt);
1413 case PARAM_HELP_REQUESTED:
1417 return CURLE_FAILED_INIT;
1420 if(passarg) /* we're supposed to skip this */
1426 helpf("only one URL is supported!\n");
1427 return CURLE_FAILED_INIT;
1433 /* if no URL was specified and there was one in the config file, get that
1435 if(!url && config->url)
1439 helpf("no URL specified!\n");
1440 return CURLE_FAILED_INIT;
1442 if(NULL == config->useragent) {
1443 /* set non-zero default values: */
1444 snprintf(useragent, sizeof(useragent),
1445 CURL_NAME "/" CURL_VERSION " (" OS ") " "%s", curl_version());
1446 config->useragent= useragent;
1449 allocuseragent = TRUE;
1451 fprintf(stderr, "URL: %s PROXY: %s\n", url, config->proxy?config->proxy:"none");
1454 /* expand '{...}' and '[...]' expressions and return total number of URLs
1456 res = glob_url(&urls, url, &urlnum);
1460 /* save outfile pattern befor expansion */
1461 outfiles = config->outfile?strdup(config->outfile):NULL;
1463 if (!outfiles && !config->remotefile && urlnum > 1) {
1464 #ifdef CURL_SEPARATORS
1465 /* multiple files extracted to stdout, insert separators! */
1468 #ifdef MIME_SEPARATORS
1469 /* multiple files extracted to stdout, insert MIME separators! */
1471 printf("MIME-Version: 1.0\n");
1472 printf("Content-Type: multipart/mixed; boundary=%s\n\n", MIMEseparator);
1475 for (i = 0; (url = next_url(urls)); ++i) {
1476 if (config->outfile) {
1477 free(config->outfile);
1478 config->outfile = outfiles?strdup(outfiles):NULL;
1481 if (config->outfile || config->remotefile) {
1483 * We have specified a file name to store the result in, or we have
1484 * decided we want to use the remote file name.
1487 if(!config->outfile && config->remotefile) {
1488 /* Find and get the remote file name */
1489 char * pc =strstr(url, "://");
1494 pc = strrchr(pc, '/');
1495 config->outfile = (char *) NULL == pc ? NULL : strdup(pc+1) ;
1496 if(!config->outfile || !strlen(config->outfile)) {
1497 helpf("Remote file name has no length!\n");
1498 return CURLE_WRITE_ERROR;
1502 /* fill '#1' ... '#9' terms from URL pattern */
1503 char *outfile = config->outfile;
1504 config->outfile = match_url(config->outfile, urls);
1508 if((0 == config->resume_from) && config->use_resume) {
1509 /* we're told to continue where we are now, then we get the size of the
1510 file as it is now and open it for append instead */
1511 struct stat fileinfo;
1513 if(0 == stat(config->outfile, &fileinfo)) {
1514 /* set offset to current file size: */
1515 config->resume_from = fileinfo.st_size;
1517 /* else let offset remain 0 */
1520 if(config->resume_from) {
1521 /* open file for output: */
1522 outs.stream=(FILE *) fopen(config->outfile, config->resume_from?"ab":"wb");
1524 helpf("Can't open '%s'!\n", config->outfile);
1525 return CURLE_WRITE_ERROR;
1529 outs.filename = config->outfile;
1530 outs.stream = NULL; /* open when needed */
1533 if (config->infile) {
1535 * We have specified a file to upload
1537 struct stat fileinfo;
1539 /* If no file name part is given in the URL, we add this file name */
1540 char *ptr=strstr(url, "://");
1545 ptr = strrchr(ptr, '/');
1546 if(!ptr || !strlen(++ptr)) {
1547 /* The URL has no file name part, add the local file name. In order to
1548 be able to do so, we have to create a new URL in another buffer.*/
1550 urlbuffer=(char *)malloc(strlen(url) + strlen(config->infile) + 3);
1552 helpf("out of memory\n");
1553 return CURLE_OUT_OF_MEMORY;
1556 /* there is a trailing slash on the URL */
1557 sprintf(urlbuffer, "%s%s", url, config->infile);
1559 /* thers is no trailing slash on the URL */
1560 sprintf(urlbuffer, "%s/%s", url, config->infile);
1562 url = urlbuffer; /* use our new URL instead! */
1565 infd=(FILE *) fopen(config->infile, "rb");
1566 if (!infd || stat(config->infile, &fileinfo)) {
1567 helpf("Can't open '%s'!\n", config->infile);
1568 return CURLE_READ_ERROR;
1570 infilesize=fileinfo.st_size;
1573 if((config->conf&CONF_UPLOAD) &&
1574 config->use_resume &&
1575 (0==config->resume_from)) {
1576 config->resume_from = -1; /* -1 will then force get-it-yourself */
1578 if(config->headerfile) {
1579 /* open file for output: */
1580 if(strcmp(config->headerfile,"-")) {
1581 heads.filename = config->headerfile;
1586 heads.stream = headerfilep;
1587 heads.config = config;
1590 if(outs.stream && isatty(fileno(outs.stream)) &&
1591 !(config->conf&(CONF_UPLOAD|CONF_HTTPPOST)))
1592 /* we send the output to a tty and it isn't an upload operation,
1593 therefore we switch off the progress meter */
1594 config->conf |= CONF_NOPROGRESS;
1598 fprintf(stderr, "\n[%d/%d]: %s --> %s\n",
1599 i+1, urlnum, url, config->outfile ? config->outfile : "<stdout>");
1601 #ifdef CURL_SEPARATORS
1602 printf("%s%s\n", CURLseparator, url);
1604 #ifdef MIME_SEPARATORS
1605 printf("--%s\n", MIMEseparator);
1606 printf("Content-ID: %s\n\n", url);
1612 config->errors = stderr;
1615 if(!config->outfile && !(config->conf & CONF_GETTEXT)) {
1616 /* We get the output to stdout and we have not got the ASCII/text flag,
1617 then set stdout to be binary */
1618 setmode( 1, O_BINARY );
1625 /* The new, v7-style easy-interface! */
1626 curl = curl_easy_init();
1628 curl_easy_setopt(curl, CURLOPT_FILE, (FILE *)&outs); /* where to store */
1629 /* what call to write: */
1630 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
1631 curl_easy_setopt(curl, CURLOPT_INFILE, infd); /* for uploads */
1632 /* size of uploaded file: */
1633 curl_easy_setopt(curl, CURLOPT_INFILESIZE, infilesize);
1634 curl_easy_setopt(curl, CURLOPT_URL, url); /* what to fetch */
1635 curl_easy_setopt(curl, CURLOPT_PROXY, config->proxy); /* proxy to use */
1636 curl_easy_setopt(curl, CURLOPT_VERBOSE, config->conf&CONF_VERBOSE);
1637 curl_easy_setopt(curl, CURLOPT_HEADER, config->conf&CONF_HEADER);
1638 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, config->conf&CONF_NOPROGRESS);
1639 curl_easy_setopt(curl, CURLOPT_NOBODY, config->conf&CONF_NOBODY);
1640 curl_easy_setopt(curl, CURLOPT_FAILONERROR,
1641 config->conf&CONF_FAILONERROR);
1642 curl_easy_setopt(curl, CURLOPT_UPLOAD, config->conf&CONF_UPLOAD);
1643 curl_easy_setopt(curl, CURLOPT_POST, config->conf&CONF_POST);
1644 curl_easy_setopt(curl, CURLOPT_FTPLISTONLY,
1645 config->conf&CONF_FTPLISTONLY);
1646 curl_easy_setopt(curl, CURLOPT_FTPAPPEND, config->conf&CONF_FTPAPPEND);
1647 curl_easy_setopt(curl, CURLOPT_NETRC, config->conf&CONF_NETRC);
1648 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,
1649 config->conf&CONF_FOLLOWLOCATION);
1650 curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, config->conf&CONF_GETTEXT);
1651 curl_easy_setopt(curl, CURLOPT_PUT, config->conf&CONF_PUT);
1652 curl_easy_setopt(curl, CURLOPT_MUTE, config->conf&CONF_MUTE);
1653 curl_easy_setopt(curl, CURLOPT_USERPWD, config->userpwd);
1654 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd);
1655 curl_easy_setopt(curl, CURLOPT_RANGE, config->range);
1656 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
1657 curl_easy_setopt(curl, CURLOPT_TIMEOUT, config->timeout);
1658 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config->postfields);
1660 /* new in libcurl 7.2: */
1661 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config->postfieldsize);
1663 curl_easy_setopt(curl, CURLOPT_REFERER, config->referer);
1664 curl_easy_setopt(curl, CURLOPT_AUTOREFERER,
1665 config->conf&CONF_AUTO_REFERER);
1666 curl_easy_setopt(curl, CURLOPT_USERAGENT, config->useragent);
1667 curl_easy_setopt(curl, CURLOPT_FTPPORT, config->ftpport);
1668 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, config->low_speed_limit);
1669 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time);
1670 curl_easy_setopt(curl, CURLOPT_RESUME_FROM,
1671 config->use_resume?config->resume_from:0);
1672 curl_easy_setopt(curl, CURLOPT_COOKIE, config->cookie);
1673 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, config->headers);
1674 curl_easy_setopt(curl, CURLOPT_HTTPPOST, config->httppost);
1675 curl_easy_setopt(curl, CURLOPT_SSLCERT, config->cert);
1676 curl_easy_setopt(curl, CURLOPT_SSLCERTPASSWD, config->cert_passwd);
1678 if(config->cacert) {
1679 /* available from libcurl 7.5: */
1680 curl_easy_setopt(curl, CURLOPT_CAINFO, config->cacert);
1681 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE);
1684 if(config->conf&(CONF_NOBODY|CONF_USEREMOTETIME)) {
1685 /* no body or use remote time */
1687 curl_easy_setopt(curl, CURLOPT_FILETIME, TRUE);
1691 if (config->maxredirs)
1692 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs);
1694 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, DEFAULT_MAXREDIRS);
1697 curl_easy_setopt(curl, CURLOPT_CRLF, config->crlf);
1698 curl_easy_setopt(curl, CURLOPT_QUOTE, config->quote);
1699 curl_easy_setopt(curl, CURLOPT_POSTQUOTE, config->postquote);
1700 curl_easy_setopt(curl, CURLOPT_WRITEHEADER,
1701 config->headerfile?&heads:NULL);
1702 curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config->cookiefile);
1703 curl_easy_setopt(curl, CURLOPT_SSLVERSION, config->ssl_version);
1704 curl_easy_setopt(curl, CURLOPT_TIMECONDITION, config->timecond);
1705 curl_easy_setopt(curl, CURLOPT_TIMEVALUE, config->condtime);
1706 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
1707 curl_easy_setopt(curl, CURLOPT_STDERR, config->errors);
1709 /* three new ones in libcurl 7.3: */
1710 curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel);
1711 curl_easy_setopt(curl, CURLOPT_INTERFACE, config->iface);
1712 curl_easy_setopt(curl, CURLOPT_KRB4LEVEL, config->krb4level);
1714 if((config->progressmode == CURL_PROGRESS_BAR) &&
1715 !(config->conf&(CONF_NOPROGRESS|CONF_MUTE))) {
1716 /* we want the alternative style, then we have to implement it
1718 progressbarinit(&progressbar);
1719 curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress);
1720 curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
1723 res = curl_easy_perform(curl);
1725 if(config->writeout) {
1726 ourWriteOut(curl, config->writeout);
1729 /* always cleanup */
1730 curl_easy_cleanup(curl);
1732 if((res!=CURLE_OK) && config->showerror)
1733 fprintf(config->errors, "curl: (%d) %s\n", res, errorbuffer);
1736 fprintf(config->errors, "curl: failed to init libcurl!\n");
1740 if((config->errors != stderr) &&
1741 (config->errors != stdout))
1742 /* it wasn't directed to stdout or stderr so close the file! */
1743 fclose(config->errors);
1745 if(config->headerfile && !headerfilep && heads.stream)
1746 fclose(heads.stream);
1750 if (config->outfile && outs.stream)
1751 fclose(outs.stream);
1755 fclose(headerfilep);
1764 #ifdef MIME_SEPARATORS
1766 printf("--%s--\n", MIMEseparator);
1770 free(config->useragent);
1772 /* cleanup memory used for URL globbing patterns */
1779 int main(int argc, char *argv[])
1782 struct Configurable config;
1783 memset(&config, 0, sizeof(struct Configurable));
1785 res = operate(&config, argc, argv);
1786 free_config_fields(&config);
1791 static char *my_get_line(FILE *fp)
1795 char *retval = NULL;
1799 if (NULL == fgets(buf, sizeof(buf), fp))
1802 retval = strdup(buf);
1805 if (NULL == (retval = realloc(retval,
1806 strlen(retval) + strlen(buf) + 1)))
1808 strcat(retval, buf);
1811 while (NULL == (nl = strchr(retval, '\n')));