d42fe11b90fdee32bebac3af66bd863883ca2633
[platform/upstream/curl.git] / src / main.c
1 /*****************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
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/
12  *
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
16  *  under the License.
17  *
18  *  The Original Code is Curl.
19  *
20  *  The Initial Developer of the Original Code is Daniel Stenberg.
21  *
22  *  Portions created by the Initial Developer are Copyright (C) 1998.
23  *  All Rights Reserved.
24  *
25  * ------------------------------------------------------------
26  * Main author:
27  * - Daniel Stenberg <daniel@haxx.se>
28  *
29  *      http://curl.haxx.se
30  *
31  * $Source$
32  * $Revision$
33  * $Date$
34  * $Author$
35  * $State$
36  * $Locker$
37  *
38  * ------------------------------------------------------------
39  ****************************************************************************/
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <stdarg.h>
45 #include <sys/stat.h>
46 #include <ctype.h>
47
48 #include <curl/curl.h>
49 #include <curl/types.h> /* new for v7 */
50 #include <curl/easy.h> /* new for v7 */
51
52 #define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
53 #include <curl/mprintf.h>
54
55 #include "urlglob.h"
56 #include "writeout.h"
57
58 #define CURLseparator   "--_curl_--"
59 #define MIMEseparator   "_curl_"
60
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
65
66 /* This is now designed to have its own local setup.h */
67 #include "setup.h"
68
69 #ifdef WIN32
70 #include <winsock.h>
71 #endif
72
73 #include "version.h"
74
75 #ifdef HAVE_IO_H /* typical win32 habit */
76 #include <io.h>
77 #endif
78
79 #ifdef HAVE_UNISTD_H
80 #include <unistd.h>
81 #endif
82
83 #ifdef HAVE_FCNTL_H
84 #include <fcntl.h>
85 #endif
86
87 /* The last #include file should be: */
88 #ifdef MALLOCDEBUG
89 /* this is low-level hard-hacking memory leak tracking shit */
90 #include "../lib/memdebug.h"
91 #endif
92
93 #define DEFAULT_MAXREDIRS  50L
94
95 #ifndef __cplusplus        /* (rabe) */
96 typedef char bool;
97 #endif                     /* (rabe) */
98
99 #define CURL_PROGRESS_STATS 0 /* default progress display */
100 #define CURL_PROGRESS_BAR   1
101
102 typedef enum {
103   HTTPREQ_UNSPEC,
104   HTTPREQ_GET,
105   HTTPREQ_HEAD,
106   HTTPREQ_POST,
107   HTTPREQ_SIMPLEPOST,
108   HTTPREQ_CUSTOM,
109   HTTPREQ_LAST
110 } HttpReq;
111
112 /* Just a set of bits */
113 #define CONF_DEFAULT  0
114
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 */
132
133 #ifndef HAVE_STRDUP
134 /* Ultrix doesn't have strdup(), so make a quick clone: */
135 char *strdup(char *str)
136 {
137   int len;
138   char *newstr;
139
140   len = strlen(str);
141   newstr = (char *) malloc((len+1)*sizeof(char));
142   if (!newstr)
143     return (char *)NULL;
144
145   strcpy(newstr,str);
146
147   return newstr;
148
149 }
150 #endif 
151
152 extern void hugehelp(void);
153
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)
159 {
160   WSACleanup();
161 }
162
163 static CURLcode win32_init(void)
164 {
165   WORD wVersionRequested;  
166   WSADATA wsaData; 
167   int err; 
168   wVersionRequested = MAKEWORD(1, 1); 
169     
170   err = WSAStartup(wVersionRequested, &wsaData); 
171     
172   if (err != 0) 
173     /* Tell the user that we couldn't find a useable */ 
174     /* winsock.dll.     */ 
175     return CURLE_FAILED_INIT; 
176     
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 */ 
181   /* requested. */ 
182     
183   if ( LOBYTE( wsaData.wVersion ) != 1 || 
184        HIBYTE( wsaData.wVersion ) != 1 ) { 
185     /* Tell the user that we couldn't find a useable */ 
186
187     /* winsock.dll. */ 
188     WSACleanup(); 
189     return CURLE_FAILED_INIT; 
190   }
191   return CURLE_OK;
192 }
193 /* The Windows Sockets DLL is acceptable. Proceed. */ 
194 #else
195 static CURLcode win32_init(void) { return CURLE_OK; }
196 #define win32_cleanup()
197 #endif
198
199
200 /*
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.
204  */
205 CURLcode main_init(void)
206 {
207   return win32_init();
208 }
209
210 /*
211  * This is the main global destructor for the app. Call this after
212  * _all_ libcurl usage is done.
213  */
214 void main_free(void)
215 {
216   win32_cleanup();
217 }
218
219 int SetHTTPrequest(HttpReq req, HttpReq *store)
220 {
221   if((*store == HTTPREQ_UNSPEC) ||
222      (*store == req)) {
223     *store = req;
224     return 0;
225   }
226   fprintf(stderr, "You can only select one HTTP request!\n");
227   return 1;
228 }
229
230 static void helpf(char *fmt, ...)
231 {
232   va_list ap;
233   if(fmt) {
234     va_start(ap, fmt);
235     fputs("curl: ", stderr); /* prefix it */
236     vfprintf(stderr, fmt, ap);
237     va_end(ap);
238   }
239   fprintf(stderr, "curl: try 'curl --help' for more information\n");
240 }
241
242 static void help(void)
243 {
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"
262
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",
303          curl_version()
304          );
305 }
306
307 struct LongShort {
308   char *letter;
309   char *lname;
310   bool extraparam;
311 };
312
313 struct Configurable {
314   char *useragent;
315   char *cookie;
316   bool use_resume;
317   int resume_from;
318   char *postfields;
319   long postfieldsize;
320   char *referer;
321   long timeout;
322   long maxredirs;
323   char *outfile;
324   char *headerfile;
325   char remotefile;
326   char *ftpport;
327   char *iface;
328   unsigned short porttouse;
329   char *range;
330   int low_speed_limit;
331   int low_speed_time;
332   bool showerror;
333   char *infile;
334   char *userpwd;
335   char *proxyuserpwd;
336   char *proxy;
337   bool configread;
338   bool proxytunnel;
339   long conf;
340   char *url;
341   char *cert;
342   char *cacert;
343   char *cert_passwd;
344   bool crlf;
345   char *cookiefile;
346   char *customrequest;
347   char *krb4level;
348   bool progressmode;
349   bool nobuffer;
350
351   char *writeout; /* %-styled format string to output */
352
353   FILE *errors; /* if stderr redirect is requested */
354
355   struct curl_slist *quote;
356   struct curl_slist *postquote;
357
358   long ssl_version;
359   TimeCond timecond;
360   time_t condtime;
361
362   struct curl_slist *headers;
363
364   struct HttpPost *httppost;
365   struct HttpPost *last_post;
366
367   HttpReq httpreq;
368 };
369
370 static int parseconfig(char *filename,
371                        struct Configurable *config);
372 static char *my_get_line(FILE *fp);
373
374 static void GetStr(char **string,
375                    char *value)
376 {
377   if(*string)
378     free(*string);
379   if(value && *value)
380     *string = strdup(value);
381   else
382     *string = NULL;
383 }
384
385 static char *file2string(FILE *file)
386 {
387   char buffer[256];
388   char *ptr;
389   char *string=NULL;
390   int len=0;
391   int stringlen;
392
393   if(file) {
394     while(fgets(buffer, sizeof(buffer), file)) {
395       ptr= strchr(buffer, '\r');
396       if(ptr)
397         *ptr=0;
398       ptr= strchr(buffer, '\n');
399       if(ptr)
400         *ptr=0;
401       stringlen=strlen(buffer);
402       if(string)
403         string = realloc(string, len+stringlen+1);
404       else
405         string = malloc(stringlen+1);
406
407       strcpy(string+len, buffer);
408
409       len+=stringlen;
410     }
411     return string;
412   }
413   else
414     return NULL; /* no string */
415 }
416
417 static char *file2memory(FILE *file, long *size)
418 {
419   char buffer[1024];
420   char *string=NULL;
421   char *newstring=NULL;
422   long len=0;
423   long stringlen=0;
424
425   if(file) {
426     while((len = fread(buffer, 1, sizeof(buffer), file))) {
427       if(string) {
428         newstring = realloc(string, len+stringlen);
429         if(newstring)
430           string = newstring;
431         else
432           break; /* no more strings attached! :-) */
433       }
434       else
435         string = malloc(len);
436       memcpy(&string[stringlen], buffer, len);
437       stringlen+=len;
438     }
439     *size = stringlen;
440     return string;
441   }
442   else
443     return NULL; /* no string */
444 }
445
446 typedef enum {
447   PARAM_OK,
448   PARAM_OPTION_AMBIGUOUS,
449   PARAM_OPTION_UNKNOWN,
450   PARAM_REQUIRES_PARAMETER,  
451   PARAM_BAD_USE,
452   PARAM_HELP_REQUESTED,
453   PARAM_GOT_EXTRA_PARAMETER,
454
455   PARAM_LAST
456 } ParameterError;
457
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
461                                                      has been used */
462                                    struct Configurable *config)
463 {
464   char letter;
465   char subletter=0; /* subletters can only occur on long options */
466
467   char *parse=NULL;
468   int res;
469   int j;
470   time_t now;
471   int hit=-1;
472   bool longopt=FALSE;
473
474   /* single-letter,
475      long-name,
476      boolean whether it takes an additional argument
477      */
478   struct LongShort aliases[]= {
479     {"9", "crlf",        FALSE},
480     {"8", "stderr",      TRUE},
481     {"7", "interface",   TRUE},
482     {"6", "krb4",        TRUE},
483     {"5", "url",         TRUE},
484
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},
494     {"d", "data",        TRUE},
495     {"da", "data-ascii", TRUE},
496     {"db", "data-binary", TRUE},
497     {"D", "dump-header", TRUE},
498     {"e", "referer",     TRUE},
499     {"E", "cert",        TRUE},
500     {"Ea", "cacert",     TRUE},
501     {"f", "fail",        FALSE},
502     {"F", "form",        TRUE},
503
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},
526     {"u", "user",        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},
539   };
540
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);
546     int numhits=0;
547     for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
548       if(strnequal(aliases[j].lname, word, fnam)) {
549         longopt = TRUE;
550         numhits++;
551         if(strequal(aliases[j].lname, word)) {
552           parse = aliases[j].letter;
553           hit = j;
554           numhits = 1; /* a single unique hit */
555           break;
556         }
557         parse = aliases[j].letter;
558         hit = j;
559       }
560     }
561     if(numhits>1) {
562       /* this is at least the second match! */
563       return PARAM_OPTION_AMBIGUOUS;
564     }
565     if(hit < 0) {
566       return PARAM_OPTION_UNKNOWN;
567     }    
568   }
569   else {
570     flag++; /* prefixed with one dash, pass it */
571     hit=-1;
572     parse = flag;
573   }
574
575   do {
576     /* we can loop here if we have multiple single-letters */
577
578     if(!longopt)
579       letter = parse?*parse:'\0';
580     else {
581       letter = parse[0];
582       subletter = parse[1];
583     }
584     *usedarg = FALSE; /* default is that we don't use the arg */
585
586 #if 0
587     fprintf(stderr, "OPTION: %c %s\n", letter, nextarg?nextarg:"<null>");
588 #endif
589     if(hit < 0) {
590       for(j=0; j< sizeof(aliases)/sizeof(aliases[0]); j++) {
591         if(letter == aliases[j].letter[0]) {
592           hit = j;
593           break;
594         }
595       }
596       if(hit < 0) {
597         return PARAM_OPTION_UNKNOWN;
598       }
599     }
600     if(hit < 0) {
601       return PARAM_OPTION_UNKNOWN;
602     }    
603     if((!nextarg || !*nextarg) && aliases[hit].extraparam) {
604       return PARAM_REQUIRES_PARAMETER;
605     }
606     else if(nextarg && aliases[hit].extraparam)
607       *usedarg = TRUE; /* mark it as used */
608
609     switch(letter) {
610     case '9': /* there is no short letter for this */
611       /* LF -> CRLF conversinon? */
612       config->crlf = TRUE;
613       break;
614     case '8': /* there is no short letter for this */
615       if(strcmp(nextarg, "-"))
616         config->errors = fopen(nextarg, "wt");
617       else
618         config->errors = stdout;
619       break;
620     case '7': /* there is no short letter for this */
621       /* interface */
622       GetStr(&config->iface, nextarg);
623       break;
624     case '6': /* there is no short letter for this */
625       /* krb4 level string */
626       GetStr(&config->krb4level, nextarg);
627       break;
628     case '5':
629       /* the URL! */
630       GetStr(&config->url, nextarg);
631       break;
632     case '#': /* added 19990617 larsa */
633       config->progressmode ^= CURL_PROGRESS_BAR;
634       break;
635     case '2': 
636       /* SSL version 2 */
637       config->ssl_version = 2;
638       break;
639     case '3': 
640       /* SSL version 2 */
641       config->ssl_version = 3;
642       break;
643     case 'a':
644       /* This makes the FTP sessions use APPE instead of STOR */
645       config->conf ^= CONF_FTPAPPEND;
646       break;
647     case 'A':
648       /* This specifies the User-Agent name */
649       GetStr(&config->useragent, nextarg);
650       break;
651     case 'b': /* cookie string coming up: */
652       if(nextarg[0] == '@') {
653         nextarg++;
654       }
655       else if(strchr(nextarg, '=')) {
656         /* A cookie string must have a =-letter */
657         GetStr(&config->cookie, nextarg);
658         break;
659       }
660       /* We have a cookie file to read from! */
661       GetStr(&config->cookiefile, nextarg);
662       break;
663     case 'B':
664       /* use ASCII/text when transfering */
665       config->conf ^= CONF_GETTEXT;
666       break;
667     case 'c':
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");
671       break;
672     case 'C':
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;
677       break;
678     case 'd':
679       /* postfield data */
680       {
681         char *postdata=NULL;
682
683         if('@' == *nextarg) {
684           /* the data begins with a '@' letter, it means that a file name
685              or - (stdin) follows */
686           FILE *file;
687
688           nextarg++; /* pass the @ */
689
690           if(strequal("-", nextarg))
691             file = stdin;
692           else 
693             file = fopen(nextarg, "r");
694
695           if(subletter == 'b') /* forced binary */
696             postdata = file2memory(file, &config->postfieldsize);
697           else
698             postdata = file2string(file);
699           if(file && (file != stdin))
700             fclose(stdin);
701         }
702         else {
703           GetStr(&postdata, nextarg);
704         }
705
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);
711           free(oldpost);
712           free(postdata);
713         }
714         else
715           config->postfields=postdata;
716       }
717       if(config->postfields)
718         config->conf |= CONF_POST;
719       if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq))
720         return PARAM_BAD_USE;
721       break;
722     case 'D':
723       /* dump-header to given file name */
724       GetStr(&config->headerfile, nextarg);
725       break;
726     case 'e':
727       {
728         char *ptr = strstr(nextarg, ";auto");
729         if(ptr) {
730           /* Automatic referer requested, this may be combined with a
731              set initial one */
732           config->conf |= CONF_AUTO_REFERER;
733           *ptr = 0; /* zero terminate here */
734         }
735         GetStr(&config->referer, nextarg);
736       }
737       break;
738     case 'E':
739       if(subletter == 'a') {
740         /* CA info PEM file */
741         GetStr(&config->cacert, nextarg);
742       }
743       else {
744         char *ptr = strchr(nextarg, ':');
745         if(ptr) {
746           /* we have a password too */
747           *ptr=0;
748           ptr++;
749           GetStr(&config->cert_passwd, ptr);
750         }
751         GetStr(&config->cert, nextarg);
752       }
753       break;
754     case 'f':
755       /* fail hard on errors  */
756       config->conf ^= CONF_FAILONERROR;
757       break;
758     case 'F':
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,
762                         &config->httppost,
763                         &config->last_post))
764         return PARAM_BAD_USE;
765       if(SetHTTPrequest(HTTPREQ_POST, &config->httpreq))
766         return PARAM_BAD_USE;
767       break;
768
769     case 'h': /* h for help */
770       help();
771       return PARAM_HELP_REQUESTED;
772     case 'H':
773       /* A custom header to append to a list */
774       config->headers = curl_slist_append(config->headers, nextarg);
775       break;
776     case 'i':
777       config->conf ^= CONF_HEADER; /* include the HTTP header as well */
778       break;
779     case 'I':
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;
784       break;
785     case 'K':
786       res = parseconfig(nextarg, config);
787       config->configread = TRUE;
788       if(res)
789         return res;
790       break;
791     case 'l':
792       config->conf ^= CONF_FTPLISTONLY; /* only list the names of the FTP dir */
793       break;
794     case 'L':
795       config->conf ^= CONF_FOLLOWLOCATION; /* Follow Location: HTTP headers */
796       break;
797     case 'm':
798       /* specified max time */
799       config->timeout = atoi(nextarg);
800       break;
801     case 'M': /* M for manual, huge help */
802       hugehelp();
803       return PARAM_HELP_REQUESTED;
804     case 'n':
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;
808       break;
809     case 'N':
810       /* disable the output I/O buffering */
811       config->nobuffer ^= 1;
812       break;
813     case 'o':
814       /* output file */
815       GetStr(&config->outfile, nextarg); /* write to this file */
816       break;
817     case 'O':
818       /* output file */
819       config->remotefile ^= TRUE;
820       break;
821     case 'P':
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!
826          */
827       GetStr(&config->ftpport, nextarg);
828       break;
829     case 'p':
830       /* proxy tunnel for non-http protocols */
831       config->proxytunnel ^= TRUE;
832       break;
833
834     case 'q': /* if used first, already taken care of, we do it like
835                  this so we don't cause an error! */
836       break;
837     case 'Q':
838       /* QUOTE command to send to FTP server */
839       if(nextarg[0] == '-') {
840         /* prefixed with a dash makes it a POST TRANSFER one */
841         nextarg++;
842         config->postquote = curl_slist_append(config->postquote, nextarg);
843       }
844       else {
845         config->quote = curl_slist_append(config->quote, nextarg);
846       }
847       break;
848     case 'r':
849       /* byte range requested */
850       GetStr(&config->range, nextarg);
851       break;
852     case 's':
853       /* don't show progress meter, don't show errors : */
854       config->conf |= (CONF_MUTE|CONF_NOPROGRESS);
855       config->showerror ^= TRUE; /* toggle off */
856       break;
857     case 'S':
858       /* show errors */
859       config->showerror ^= TRUE; /* toggle on if used with -s */
860       break;
861     case 't':
862       /* we are uploading */
863       config->conf ^= CONF_UPLOAD;
864       fprintf(stderr, "-t is a deprecated switch, use '-T -' instead!\n");
865       break;
866     case 'T':
867       /* we are uploading */
868       config->conf |= CONF_UPLOAD;
869       if(!strequal("-", nextarg))
870         /* make - equal stdin */
871         GetStr(&config->infile, nextarg);
872       break;
873     case 'u':
874       /* user:password  */
875       GetStr(&config->userpwd, nextarg);
876       break;
877     case 'U':
878       /* Proxy user:password  */
879       GetStr(&config->proxyuserpwd, nextarg);
880       break;
881     case 'v':
882       config->conf ^= CONF_VERBOSE; /* talk a lot */
883       break;
884     case 'V':
885       printf(CURL_ID "%s\n", curl_version());
886       return PARAM_HELP_REQUESTED;
887     case 'w':
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 */
892         FILE *file;
893         nextarg++; /* pass the @ */
894         if(strequal("-", nextarg))
895           file = stdin;
896         else 
897           file = fopen(nextarg, "r");
898         config->writeout = file2string(file);
899         if(file && (file != stdin))
900           fclose(stdin);
901       }
902       else 
903         GetStr(&config->writeout, nextarg);
904       break;
905     case 'x':
906       /* proxy */
907       GetStr(&config->proxy, nextarg);
908       break;
909     case 'X':
910       /* HTTP request */
911       GetStr(&config->customrequest, nextarg);
912       if(SetHTTPrequest(HTTPREQ_CUSTOM, &config->httpreq))
913         return PARAM_BAD_USE;
914       break;
915     case 'y':
916       /* low speed time */
917       config->low_speed_time = atoi(nextarg);
918       if(!config->low_speed_limit)
919         config->low_speed_limit = 1;
920       break;
921     case 'Y':
922       /* low speed limit */
923       config->low_speed_limit = atoi(nextarg);
924       if(!config->low_speed_time)
925         config->low_speed_time=30;
926       break;
927     case 'z': /* time condition coming up */
928       switch(*nextarg) {
929       case '+':
930         nextarg++;
931       default:
932         /* If-Modified-Since: (section 14.28 in RFC2068) */
933         config->timecond = TIMECOND_IFMODSINCE;
934         break;
935       case '-':
936         /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
937         config->timecond = TIMECOND_IFUNMODSINCE;
938         nextarg++;
939         break;
940       case '=':
941         /* Last-Modified:  (section 14.29 in RFC2068) */
942         config->timecond = TIMECOND_LASTMOD;
943         nextarg++;
944         break;
945       }
946       now=time(NULL);
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! */
950         struct stat statbuf;
951         if(-1 == stat(nextarg, &statbuf)) {
952           /* failed, remove time condition */
953           config->timecond = TIMECOND_NONE;
954         }
955         else {
956           /* pull the time out from the file */
957           config->condtime = statbuf.st_mtime;
958         }
959       }
960       break;
961     case 'Z':
962       /* specified max no of redirects (http(s)) */
963       config->maxredirs = atoi(nextarg);
964       break;
965
966     default: /* unknown flag */
967       return PARAM_OPTION_UNKNOWN;
968     }
969     hit = -1;
970
971   } while(*++parse && !*usedarg);
972
973   return PARAM_OK;
974 }
975
976
977 static int parseconfig(char *filename,
978                        struct Configurable *config)
979 {
980   int res;
981   FILE *file;
982   char filebuffer[256];
983   bool usedarg;
984   char *home=NULL;
985   
986   if(!filename || !*filename) {
987     /* NULL or no file name attempts to load .curlrc from the homedir! */
988
989 #define CURLRC DOT_CHAR "curlrc"
990
991     home = curl_getenv("HOME"); /* portable environment reader */
992
993     if(!home)
994       return 0;
995     if(strlen(home)>(sizeof(filebuffer)-strlen(CURLRC))) {
996       free(home);
997       return 0;
998     }
999
1000     sprintf(filebuffer, "%s%s%s", home, DIR_CHAR, CURLRC);
1001
1002     filename = filebuffer;
1003   }
1004
1005   if(strcmp(filename,"-"))
1006     file = fopen(filename, "r");
1007   else
1008     file = stdin;
1009   
1010   if(file) {
1011     char *line;
1012     char *aline;
1013     char *option;
1014     char *param;
1015     int lineno=0;
1016     bool alloced_param;
1017
1018 #define isseparator(x) (((x)=='=') || ((x) == ':'))
1019
1020     while (NULL != (aline = my_get_line(file))) {
1021       lineno++;
1022       line = aline;
1023       alloced_param=FALSE;
1024
1025       /* lines with # in the fist column is a comment! */
1026       while(isspace((int)*line))
1027         line++;
1028
1029       switch(*line) {
1030       case '#':
1031       case '/':
1032       case '\r':
1033       case '\n':
1034       case '*':
1035       case '\0':
1036         free(line);
1037         continue;
1038       }
1039
1040       /* the option keywords starts here */
1041       option = line;
1042       while(*line && !isspace((int)*line) && !isseparator(*line))
1043         line++;
1044       /* ... and has ended here */
1045
1046       *line++=0; /* zero terminate, we have a local copy of the data */
1047
1048 #ifdef DEBUG_CONFIG
1049       fprintf(stderr, "GOT: %s\n", option);
1050 #endif
1051
1052       /* pass spaces and separator(s) */
1053       while(isspace((int)*line) || isseparator(*line))
1054         line++;
1055       
1056       /* the parameter starts here (unless quoted) */
1057       if(*line == '\"') {
1058         char *ptr;
1059         /* quoted parameter, do the qoute dance */
1060         line++;
1061         param=strdup(line); /* parameter */
1062         alloced_param=TRUE;
1063
1064         ptr=param;
1065         while(*line && (*line != '\"')) {
1066           if(*line == '\\') {
1067             char out;
1068             line++;
1069
1070             /* default is to output the letter after the backslah */
1071             switch(out = *line) {
1072             case '\0':
1073               continue; /* this'll break out of the loop */
1074             case 't':
1075               out='\t';
1076               break;
1077             case 'n':
1078               out='\n';
1079               break;
1080             case 'r':
1081               out='\r';
1082               break;
1083             case 'v':
1084               out='\v';
1085               break;
1086             }
1087             *ptr++=out;
1088             line++;
1089           }
1090           else
1091             *ptr++=*line++;
1092         }
1093         *ptr=0; /* always zero terminate */
1094
1095       }
1096       else {
1097         param=line; /* parameter starts here */
1098         while(*line && !isspace((int)*line))
1099           line++;
1100         *line=0; /* zero terminate */
1101       }
1102 #ifdef DEBUG_CONFIG
1103       fprintf(stderr, "PARAM: \"%s\"\n", param);
1104 #endif
1105       res = getparameter(option, param, &usedarg, config);
1106
1107       if(*param && !usedarg)
1108         /* we passed in a parameter that wasn't used! */
1109         res = PARAM_GOT_EXTRA_PARAMETER;
1110
1111       if(res != PARAM_OK) {
1112         /* the help request isn't really an error */
1113         if(!strcmp(filename, "-")) {
1114           filename="<stdin>";
1115         }
1116         if(PARAM_HELP_REQUESTED != res) {
1117           char *reason;
1118           switch(res) {
1119           default:
1120           case PARAM_GOT_EXTRA_PARAMETER:
1121             reason = "had unsupported trailing garbage";
1122             break;
1123           case PARAM_OPTION_UNKNOWN:
1124             reason = "is unknown";
1125             break;
1126           case PARAM_OPTION_AMBIGUOUS:
1127             reason = "is ambiguous";
1128             break;
1129           case PARAM_REQUIRES_PARAMETER:
1130             reason = "requires parameter";
1131             break;
1132           case PARAM_BAD_USE:
1133             reason = "is badly used here";
1134             break;
1135           }
1136           fprintf(stderr, "%s:%d: warning: '%s' %s\n",
1137                   filename, lineno, option, reason);
1138         }
1139       }
1140
1141       if(alloced_param)
1142         free(param);
1143
1144       free(aline);
1145     }
1146     if(file != stdin)
1147       fclose(file);
1148   }
1149   if(home)
1150     free(home);
1151   return 0;
1152 }
1153
1154 struct OutStruct {
1155   char *filename;
1156   FILE *stream;
1157   struct Configurable *config;
1158 };
1159
1160 int my_fwrite(void *buffer, size_t size, size_t nmemb, FILE *stream)
1161 {
1162   struct OutStruct *out=(struct OutStruct *)stream;
1163   if(out && !out->stream) {
1164     /* open file for writing */
1165     out->stream=fopen(out->filename, "wb");
1166     if(!out->stream)
1167       return -1; /* failure */
1168     if(out->config->nobuffer) {
1169       /* disable output buffering */
1170 #ifdef HAVE_SETVBUF
1171       setvbuf(out->stream, NULL, _IONBF, 0);
1172 #endif
1173     }
1174   }
1175   return fwrite(buffer, size, nmemb, out->stream);
1176 }
1177
1178 struct ProgressData {
1179   size_t total;
1180   size_t prev;
1181   size_t point;
1182   int width;
1183 };
1184
1185 int myprogress (void *clientp,
1186                 size_t dltotal,
1187                 size_t dlnow,
1188                 size_t ultotal,
1189                 size_t ulnow)
1190 {
1191   /* The original progress-bar source code was written for curl by Lars Aas,
1192      and this new edition inherites some of his concepts. */
1193   
1194   char line[256];
1195   char outline[256];
1196   char format[40];
1197   float frac;
1198   float percent;
1199   int barwidth;
1200   int num;
1201   int i;
1202
1203   struct ProgressData *bar = (struct ProgressData *)clientp;
1204   size_t total = dltotal + ultotal;
1205
1206   bar->point = dlnow + ulnow; /* we've come this far */
1207
1208   if(0 == total) {
1209     int prevblock = bar->prev / 1024;
1210     int thisblock = bar->point / 1024;
1211     while ( thisblock > prevblock ) {
1212       fprintf( stderr, "#" );
1213       prevblock++;
1214     }
1215   }
1216   else {
1217     frac = (float) bar->point / (float) total;
1218     percent = frac * 100.0f;
1219     barwidth = bar->width - 7;
1220     num = (int) (((float)barwidth) * frac);
1221     i = 0;
1222     for ( i = 0; i < num; i++ ) {
1223       line[i] = '#';
1224     }
1225     line[i] = '\0';
1226     sprintf( format, "%%-%ds %%5.1f%%%%", barwidth );
1227     sprintf( outline, format, line, percent );
1228     fprintf( stderr, "\r%s", outline );
1229   }
1230   bar->prev = bar->point;
1231
1232   return 0;
1233 }
1234
1235 void progressbarinit(struct ProgressData *bar)
1236 {
1237 #ifdef __EMX__
1238   /* 20000318 mgs */
1239   int scr_size [2];
1240 #endif
1241   char *colp;
1242
1243   memset(bar, 0, sizeof(struct ProgressData));
1244
1245 /* TODO: get terminal width through ansi escapes or something similar.
1246          try to update width when xterm is resized... - 19990617 larsa */
1247 #ifndef __EMX__
1248   /* 20000318 mgs
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");
1252   if (colp != NULL) {
1253     bar->width = atoi(colp);
1254     free(colp);
1255   }
1256   else
1257     bar->width = 79;
1258 #else
1259   /* 20000318 mgs
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. */
1264  
1265   _scrsize(scr_size);
1266   bar->width = scr_size[0] - 1;
1267 #endif
1268
1269 }
1270
1271 void free_config_fields(struct Configurable *config)
1272 {
1273   if(config->url)
1274     free(config->url);
1275   if(config->userpwd)
1276     free(config->userpwd);
1277   if(config->postfields)
1278     free(config->postfields);
1279   if(config->proxy)
1280     free(config->proxy);
1281   if(config->proxyuserpwd)
1282     free(config->proxyuserpwd);
1283   if(config->cookie)
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);
1291   if(config->outfile)
1292     free(config->outfile);
1293   if(config->ftpport)
1294     free(config->ftpport);
1295   if(config->infile)
1296     free(config->infile);
1297   if(config->range)
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);
1305   if(config->cacert)
1306     free(config->cacert);
1307
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); /*  */
1311 }
1312
1313
1314 static int 
1315 operate(struct Configurable *config, int argc, char *argv[])
1316 {
1317   char errorbuffer[CURL_ERROR_SIZE];
1318   char useragent[128]; /* buah, we don't want a larger default user agent */
1319   struct ProgressData progressbar;
1320
1321   struct OutStruct outs;
1322   struct OutStruct heads;
1323
1324   char *url = NULL;
1325
1326   URLGlob *urls;
1327   int urlnum;
1328   char *outfiles;
1329   int separator = 0;
1330   
1331   FILE *infd = stdin;
1332   FILE *headerfilep = NULL;
1333   char *urlbuffer=NULL;
1334   int infilesize=-1; /* -1 means unknown */
1335   bool stillflags=TRUE;
1336
1337   bool allocuseragent=FALSE;
1338
1339   CURL *curl;
1340   int res;
1341   int i;
1342
1343   outs.stream = stdout;
1344   outs.config = config;
1345
1346 #ifdef MALLOCDEBUG
1347   /* this sends all memory debug messages to a logfile named memdump */
1348   curl_memdebug("memdump");
1349 #endif
1350
1351   config->showerror=TRUE;
1352   config->conf=CONF_DEFAULT;
1353 #if 0
1354   config->crlf=FALSE;
1355   config->quote=NULL;
1356 #endif
1357
1358   if(argc>1 &&
1359      (!strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
1360      strchr(argv[1], 'q')) {
1361     /*
1362      * The first flag, that is not a verbose name, but a shortname
1363      * and it includes the 'q' flag!
1364      */
1365 #if 0
1366     fprintf(stderr, "I TURNED OFF THE CRAP\n");
1367 #endif
1368     ;
1369   }
1370   else {
1371     res = parseconfig(NULL, config);
1372     if(res)
1373       return res;
1374   }
1375
1376   if ((argc < 2)  && !config->url) {
1377     helpf(NULL);
1378     return CURLE_FAILED_INIT;
1379   }
1380
1381   /* Parse options */
1382   for (i = 1; i < argc; i++) {
1383     if(stillflags &&
1384        ('-' == argv[i][0])) {
1385       char *nextarg;
1386       bool passarg;
1387       char *origopt=argv[i];
1388       
1389       char *flag = argv[i];
1390
1391       if(strequal("--", argv[i]))
1392         /* this indicates the end of the flags and thus enables the
1393            following (URL) argument to start with -. */
1394         stillflags=FALSE;
1395       else {
1396         nextarg= (i < argc - 1)? argv[i+1]: NULL;
1397
1398         res = getparameter(flag, nextarg, &passarg, config);
1399         if(res) {
1400           switch(res) {
1401           case PARAM_OPTION_AMBIGUOUS:
1402             helpf("option %s is ambiguous\n", origopt);
1403             break;
1404           case PARAM_OPTION_UNKNOWN:
1405             helpf("option %s is unknown\n", origopt);
1406             break;
1407           case PARAM_REQUIRES_PARAMETER:
1408             helpf("option %s requires an extra argument!\n", origopt);
1409             break;
1410           case PARAM_BAD_USE:
1411             helpf("option %s was wrongly used!\n", origopt);
1412             break;
1413           case PARAM_HELP_REQUESTED:
1414             /* no text */
1415             break;
1416           }
1417           return CURLE_FAILED_INIT;
1418         }
1419
1420         if(passarg) /* we're supposed to skip this */
1421           i++;
1422       }
1423     }
1424     else {
1425       if(url) {
1426         helpf("only one URL is supported!\n");
1427         return CURLE_FAILED_INIT;
1428       }
1429       url = argv[i];
1430     }
1431   }
1432
1433   /* if no URL was specified and there was one in the config file, get that
1434      one */
1435   if(!url && config->url)
1436     url = config->url;
1437   
1438   if(!url) {
1439     helpf("no URL specified!\n");
1440     return CURLE_FAILED_INIT;
1441   }
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;
1447   }
1448   else
1449     allocuseragent = TRUE;
1450 #if 0
1451   fprintf(stderr, "URL: %s PROXY: %s\n", url, config->proxy?config->proxy:"none");
1452 #endif
1453
1454   /* expand '{...}' and '[...]' expressions and return total number of URLs
1455      in pattern set */
1456   res = glob_url(&urls, url, &urlnum);
1457   if(res != CURLE_OK)
1458     return res;
1459
1460   /* save outfile pattern befor expansion */
1461   outfiles = config->outfile?strdup(config->outfile):NULL;
1462
1463   if (!outfiles && !config->remotefile && urlnum > 1) {
1464 #ifdef CURL_SEPARATORS
1465     /* multiple files extracted to stdout, insert separators! */
1466     separator = 1;
1467 #endif
1468 #ifdef MIME_SEPARATORS
1469     /* multiple files extracted to stdout, insert MIME separators! */
1470     separator = 1;
1471     printf("MIME-Version: 1.0\n");
1472     printf("Content-Type: multipart/mixed; boundary=%s\n\n", MIMEseparator);
1473 #endif
1474   }
1475   for (i = 0; (url = next_url(urls)); ++i) {
1476     if (config->outfile) {
1477       free(config->outfile);
1478       config->outfile = outfiles?strdup(outfiles):NULL;
1479     }
1480  
1481     if (config->outfile || config->remotefile) {
1482       /* 
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.
1485        */
1486       
1487       if(!config->outfile && config->remotefile) {
1488         /* Find and get the remote file name */
1489         char * pc =strstr(url, "://");
1490         if(pc)
1491           pc+=3;
1492         else
1493           pc=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;
1499         }
1500       }
1501       else {
1502         /* fill '#1' ... '#9' terms from URL pattern */
1503         char *outfile = config->outfile;
1504         config->outfile = match_url(config->outfile, urls);
1505         free(outfile);
1506       }
1507       
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;
1512
1513         if(0 == stat(config->outfile, &fileinfo)) {
1514           /* set offset to current file size: */
1515           config->resume_from = fileinfo.st_size;
1516         }
1517         /* else let offset remain 0 */
1518       }
1519       
1520       if(config->resume_from) {
1521         /* open file for output: */
1522         outs.stream=(FILE *) fopen(config->outfile, config->resume_from?"ab":"wb");
1523         if (!outs.stream) {
1524           helpf("Can't open '%s'!\n", config->outfile);
1525           return CURLE_WRITE_ERROR;
1526         }
1527       }
1528       else {
1529         outs.filename = config->outfile;
1530         outs.stream = NULL; /* open when needed */
1531       }
1532     }
1533     if (config->infile) {
1534       /*
1535        * We have specified a file to upload
1536        */
1537       struct stat fileinfo;
1538
1539       /* If no file name part is given in the URL, we add this file name */
1540       char *ptr=strstr(url, "://");
1541       if(ptr)
1542         ptr+=3;
1543       else
1544         ptr=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.*/
1549
1550         urlbuffer=(char *)malloc(strlen(url) + strlen(config->infile) + 3);
1551         if(!urlbuffer) {
1552           helpf("out of memory\n");
1553           return CURLE_OUT_OF_MEMORY;
1554         }
1555         if(ptr)
1556           /* there is a trailing slash on the URL */
1557           sprintf(urlbuffer, "%s%s", url, config->infile);
1558         else
1559           /* thers is no trailing slash on the URL */
1560           sprintf(urlbuffer, "%s/%s", url, config->infile);
1561         
1562         url = urlbuffer; /* use our new URL instead! */
1563       }
1564
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;
1569       }
1570       infilesize=fileinfo.st_size;
1571       
1572     }
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 */
1577     }
1578     if(config->headerfile) {
1579       /* open file for output: */
1580       if(strcmp(config->headerfile,"-")) {
1581         heads.filename = config->headerfile;
1582         headerfilep=NULL;
1583       }
1584       else
1585         headerfilep=stdout;
1586       heads.stream = headerfilep;
1587       heads.config = config;
1588     }
1589     
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;
1595     
1596
1597     if (urlnum > 1) {
1598       fprintf(stderr, "\n[%d/%d]: %s --> %s\n",
1599               i+1, urlnum, url, config->outfile ? config->outfile : "<stdout>");
1600       if (separator) {
1601 #ifdef CURL_SEPARATORS
1602         printf("%s%s\n", CURLseparator, url);
1603 #endif
1604 #ifdef MIME_SEPARATORS
1605         printf("--%s\n", MIMEseparator);
1606         printf("Content-ID: %s\n\n", url); 
1607 #endif
1608       }
1609     }
1610
1611     if(!config->errors)
1612       config->errors = stderr;
1613
1614 #ifdef WIN32
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 );
1619     }
1620 #endif
1621
1622
1623     main_init();
1624
1625     /* The new, v7-style easy-interface! */
1626     curl = curl_easy_init();
1627     if(curl) {
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);
1659       
1660       /* new in libcurl 7.2: */
1661       curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config->postfieldsize);
1662
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);
1677
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);
1682       }
1683
1684       if(config->conf&(CONF_NOBODY|CONF_USEREMOTETIME)) {
1685         /* no body or use remote time */
1686         /* new in 7.5 */
1687         curl_easy_setopt(curl, CURLOPT_FILETIME, TRUE);
1688       }
1689       
1690       /* 7.5 news: */
1691       if (config->maxredirs) 
1692         curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs); 
1693       else 
1694         curl_easy_setopt(curl, CURLOPT_MAXREDIRS, DEFAULT_MAXREDIRS); 
1695  
1696
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);
1708       
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);
1713
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
1717            ourselves! */
1718         progressbarinit(&progressbar);
1719         curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, myprogress);
1720         curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
1721       }
1722
1723       res = curl_easy_perform(curl);
1724
1725       if(config->writeout) {
1726         ourWriteOut(curl, config->writeout);
1727       }
1728
1729       /* always cleanup */
1730       curl_easy_cleanup(curl);
1731
1732       if((res!=CURLE_OK) && config->showerror)
1733         fprintf(config->errors, "curl: (%d) %s\n", res, errorbuffer);
1734     }
1735     else
1736       fprintf(config->errors, "curl: failed to init libcurl!\n");
1737
1738     main_free();
1739
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);
1744     
1745     if(config->headerfile && !headerfilep && heads.stream)
1746       fclose(heads.stream);
1747
1748     if(urlbuffer)
1749       free(urlbuffer);
1750     if (config->outfile && outs.stream)
1751       fclose(outs.stream);
1752     if (config->infile)
1753       fclose(infd);
1754     if(headerfilep)
1755       fclose(headerfilep);
1756     
1757     if(url)
1758       free(url);
1759
1760   }
1761   if(outfiles)
1762     free(outfiles);
1763
1764 #ifdef MIME_SEPARATORS
1765   if (separator)
1766     printf("--%s--\n", MIMEseparator);
1767 #endif
1768
1769   if(allocuseragent)
1770     free(config->useragent);
1771
1772   /* cleanup memory used for URL globbing patterns */
1773   glob_cleanup(urls);
1774
1775   return res;
1776 }
1777
1778
1779 int main(int argc, char *argv[])
1780 {
1781   int res;
1782   struct Configurable config;
1783   memset(&config, 0, sizeof(struct Configurable));
1784   
1785   res = operate(&config, argc, argv);
1786   free_config_fields(&config);
1787
1788   return res;
1789 }
1790
1791 static char *my_get_line(FILE *fp)
1792 {
1793    char buf[4096];
1794    char *nl = NULL;
1795    char *retval = NULL;
1796
1797    do
1798    {
1799       if (NULL == fgets(buf, sizeof(buf), fp))
1800          break;
1801       if (NULL == retval)
1802          retval = strdup(buf);
1803       else
1804       {
1805          if (NULL == (retval = realloc(retval,
1806                                        strlen(retval) + strlen(buf) + 1)))
1807             break;
1808          strcat(retval, buf);
1809       }
1810    }
1811    while (NULL == (nl = strchr(retval, '\n')));
1812
1813    if (NULL != nl)
1814      *nl = '\0';
1815
1816    return retval;
1817 }