curl tool: reviewed code moved to tool_*.[ch] files
[platform/upstream/curl.git] / src / tool_operate.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22 #include "setup.h"
23
24 #include <curl/curl.h>
25
26 #ifdef HAVE_UNISTD_H
27 #  include <unistd.h>
28 #endif
29
30 #ifdef HAVE_FCNTL_H
31 #  include <fcntl.h>
32 #endif
33
34 #ifdef HAVE_UTIME_H
35 #  include <utime.h>
36 #elif defined(HAVE_SYS_UTIME_H)
37 #  include <sys/utime.h>
38 #endif
39
40 #ifdef HAVE_LOCALE_H
41 #  include <locale.h>
42 #endif
43
44 #include "rawstr.h"
45
46 #define ENABLE_CURLX_PRINTF
47 /* use our own printf() functions */
48 #include "curlx.h"
49
50 #include "curlutil.h"
51 #include "homedir.h"
52 #include "writeout.h"
53 #include "xattr.h"
54
55 #ifdef USE_ENVIRONMENT
56 #  include "writeenv.h"
57 #endif
58
59 #include "tool_binmode.h"
60 #include "tool_cfgable.h"
61 #include "tool_cb_dbg.h"
62 #include "tool_cb_hdr.h"
63 #include "tool_cb_prg.h"
64 #include "tool_cb_rea.h"
65 #include "tool_cb_see.h"
66 #include "tool_cb_skt.h"
67 #include "tool_cb_wrt.h"
68 #include "tool_dirhie.h"
69 #include "tool_doswin.h"
70 #include "tool_easysrc.h"
71 #include "tool_getparam.h"
72 #include "tool_helpers.h"
73 #include "tool_libinfo.h"
74 #include "tool_main.h"
75 #include "tool_msgs.h"
76 #include "tool_operate.h"
77 #include "tool_operhlp.h"
78 #include "tool_parsecfg.h"
79 #include "tool_setopt.h"
80 #include "tool_sleep.h"
81 #include "tool_urlglob.h"
82
83 #include "memdebug.h" /* keep this as LAST include */
84
85 #define CURLseparator  "--_curl_--"
86
87 #ifndef O_BINARY
88 /* since O_BINARY as used in bitmasks, setting it to zero makes it usable in
89    source code but yet it doesn't ruin anything */
90 #  define O_BINARY 0
91 #endif
92
93 #define CURL_CA_CERT_ERRORMSG1                                              \
94   "More details here: http://curl.haxx.se/docs/sslcerts.html\n\n"           \
95   "curl performs SSL certificate verification by default, "                 \
96   "using a \"bundle\"\n"                                                    \
97   " of Certificate Authority (CA) public keys (CA certs). If the default\n" \
98   " bundle file isn't adequate, you can specify an alternate file\n"        \
99   " using the --cacert option.\n"
100
101 #define CURL_CA_CERT_ERRORMSG2                                              \
102   "If this HTTPS server uses a certificate signed by a CA represented in\n" \
103   " the bundle, the certificate verification probably failed due to a\n"    \
104   " problem with the certificate (it might be expired, or the name might\n" \
105   " not match the domain name in the URL).\n"                               \
106   "If you'd like to turn off curl's verification of the certificate, use\n" \
107   " the -k (or --insecure) option.\n"
108
109 int operate(struct Configurable *config, int argc, argv_item_t argv[])
110 {
111   char errorbuffer[CURL_ERROR_SIZE];
112   struct ProgressData progressbar;
113   struct getout *urlnode;
114
115   struct OutStruct heads;
116
117   CURL *curl = NULL;
118   char *httpgetfields = NULL;
119
120   bool stillflags;
121   int res = 0;
122   int i;
123
124   errorbuffer[0] = '\0';
125   /* default headers output stream is stdout */
126   memset(&heads, 0, sizeof(struct OutStruct));
127   heads.stream = stdout;
128   heads.config = config;
129
130   memory_tracking_init();
131
132   /*
133   ** Initialize curl library - do not call any libcurl functions before
134   ** this point. Note that the memory_tracking_init() magic above is an
135   ** exception, but then that's not part of the official public API.
136   */
137   if(main_init() != CURLE_OK) {
138     helpf(config->errors, "error initializing curl library\n");
139     return CURLE_FAILED_INIT;
140   }
141
142   /* Get libcurl info right away */
143   if(get_libcurl_info() != CURLE_OK) {
144     helpf(config->errors, "error retrieving curl library information\n");
145     main_free();
146     return CURLE_FAILED_INIT;
147   }
148
149   /* Get a curl handle to use for all forthcoming curl transfers */
150   curl = curl_easy_init();
151   if(!curl) {
152     helpf(config->errors, "error initializing curl easy handle\n");
153     main_free();
154     return CURLE_FAILED_INIT;
155   }
156   config->easy = curl;
157
158   /*
159   ** Beyond this point no return'ing from this function allowed.
160   ** Jump to label 'quit_curl' in order to abandon this function
161   ** from outside of nested loops further down below.
162   */
163
164   /* setup proper locale from environment */
165 #ifdef HAVE_SETLOCALE
166   setlocale(LC_ALL, "");
167 #endif
168
169   /* inits */
170   config->postfieldsize = -1;
171   config->showerror = TRUE;
172   config->use_httpget = FALSE;
173   config->create_dirs = FALSE;
174   config->maxredirs = DEFAULT_MAXREDIRS;
175   config->proto = CURLPROTO_ALL; /* FIXME: better to read from library */
176   config->proto_present = FALSE;
177   config->proto_redir =
178     CURLPROTO_ALL & ~(CURLPROTO_FILE|CURLPROTO_SCP); /* not FILE or SCP */
179   config->proto_redir_present = FALSE;
180
181   if((argc > 1) &&
182      (!curlx_strnequal("--", argv[1], 2) && (argv[1][0] == '-')) &&
183      strchr(argv[1], 'q')) {
184     /*
185      * The first flag, that is not a verbose name, but a shortname
186      * and it includes the 'q' flag!
187      */
188     ;
189   }
190   else {
191     parseconfig(NULL, config); /* ignore possible failure */
192   }
193
194   if((argc < 2)  && !config->url_list) {
195     helpf(config->errors, NULL);
196     res = CURLE_FAILED_INIT;
197     goto quit_curl;
198   }
199
200   /* Parse options */
201   for(i = 1, stillflags = TRUE; i < argc; i++) {
202     if(stillflags &&
203        ('-' == argv[i][0])) {
204       char *nextarg;
205       bool passarg;
206       char *origopt = argv[i];
207
208       char *flag = argv[i];
209
210       if(curlx_strequal("--", argv[i]))
211         /* this indicates the end of the flags and thus enables the
212            following (URL) argument to start with -. */
213         stillflags = FALSE;
214       else {
215         nextarg = (i < (argc-1)) ? argv[i+1] : NULL;
216
217         res = getparameter(flag, nextarg, &passarg, config);
218         if(res) {
219           int retval = CURLE_OK;
220           if(res != PARAM_HELP_REQUESTED) {
221             const char *reason = param2text(res);
222             helpf(config->errors, "option %s: %s\n", origopt, reason);
223             retval = CURLE_FAILED_INIT;
224           }
225           res = retval;
226           goto quit_curl;
227         }
228
229         if(passarg) /* we're supposed to skip this */
230           i++;
231       }
232     }
233     else {
234       bool used;
235       /* just add the URL please */
236       res = getparameter((char *)"--url", argv[i], &used, config);
237       if(res)
238         goto quit_curl;
239     }
240   }
241
242   if((!config->url_list || !config->url_list->url) && !config->list_engines) {
243     helpf(config->errors, "no URL specified!\n");
244     res = CURLE_FAILED_INIT;
245     goto quit_curl;
246   }
247
248   if(!config->useragent)
249     config->useragent = my_useragent();
250   if(!config->useragent) {
251     helpf(config->errors, "out of memory\n");
252     res = CURLE_OUT_OF_MEMORY;
253     goto quit_curl;
254   }
255
256   /* On WIN32 we can't set the path to curl-ca-bundle.crt
257    * at compile time. So we look here for the file in two ways:
258    * 1: look at the environment variable CURL_CA_BUNDLE for a path
259    * 2: if #1 isn't found, use the windows API function SearchPath()
260    *    to find it along the app's path (includes app's dir and CWD)
261    *
262    * We support the environment variable thing for non-Windows platforms
263    * too. Just for the sake of it.
264    */
265   if(!config->cacert &&
266      !config->capath &&
267      !config->insecure_ok) {
268     char *env;
269     env = curlx_getenv("CURL_CA_BUNDLE");
270     if(env) {
271       config->cacert = strdup(env);
272       if(!config->cacert) {
273         curl_free(env);
274         helpf(config->errors, "out of memory\n");
275         res = CURLE_OUT_OF_MEMORY;
276         goto quit_curl;
277       }
278     }
279     else {
280       env = curlx_getenv("SSL_CERT_DIR");
281       if(env) {
282         config->capath = strdup(env);
283         if(!config->capath) {
284           curl_free(env);
285           helpf(config->errors, "out of memory\n");
286           res = CURLE_OUT_OF_MEMORY;
287           goto quit_curl;
288         }
289       }
290       else {
291         env = curlx_getenv("SSL_CERT_FILE");
292         if(env) {
293           config->cacert = strdup(env);
294           if(!config->cacert) {
295             curl_free(env);
296             helpf(config->errors, "out of memory\n");
297             res = CURLE_OUT_OF_MEMORY;
298             goto quit_curl;
299           }
300         }
301       }
302     }
303
304     if(env)
305       curl_free(env);
306 #ifdef WIN32
307     else {
308       res = FindWin32CACert(config, "curl-ca-bundle.crt");
309       if(res)
310         goto quit_curl;
311     }
312 #endif
313   }
314
315   if(config->postfields) {
316     if(config->use_httpget) {
317       /* Use the postfields data for a http get */
318       httpgetfields = strdup(config->postfields);
319       Curl_safefree(config->postfields);
320       if(!httpgetfields) {
321         helpf(config->errors, "out of memory\n");
322         res = CURLE_OUT_OF_MEMORY;
323         goto quit_curl;
324       }
325       if(SetHTTPrequest(config,
326                         (config->no_body?HTTPREQ_HEAD:HTTPREQ_GET),
327                         &config->httpreq)) {
328         res = PARAM_BAD_USE;
329         goto quit_curl;
330       }
331     }
332     else {
333       if(SetHTTPrequest(config, HTTPREQ_SIMPLEPOST, &config->httpreq)) {
334         res = PARAM_BAD_USE;
335         goto quit_curl;
336       }
337     }
338   }
339
340   /* This is the first entry added to easysrc and it initializes the slist */
341   easysrc = curl_slist_append(easysrc, "CURL *hnd = curl_easy_init();");
342   if(!easysrc) {
343     helpf(config->errors, "out of memory\n");
344     res = CURLE_OUT_OF_MEMORY;
345     goto quit_curl;
346   }
347
348   if(config->list_engines) {
349     struct curl_slist *engines = NULL;
350     curl_easy_getinfo(curl, CURLINFO_SSL_ENGINES, &engines);
351     list_engines(engines);
352     curl_slist_free_all(engines);
353     res = CURLE_OK;
354     goto quit_curl;
355   }
356
357   /* Single header file for all URLs */
358   if(config->headerfile) {
359     /* open file for output: */
360     if(!curlx_strequal(config->headerfile, "-")) {
361       FILE *newfile = fopen(config->headerfile, "wb");
362       if(!newfile) {
363         warnf(config, "Failed to open %s\n", config->headerfile);
364         res = CURLE_WRITE_ERROR;
365         goto quit_curl;
366       }
367       else {
368         heads.filename = config->headerfile;
369         heads.s_isreg = TRUE;
370         heads.fopened = TRUE;
371         heads.stream = newfile;
372       }
373     }
374   }
375
376   /*
377   ** Nested loops start here.
378   */
379
380   /* loop through the list of given URLs */
381
382   for(urlnode = config->url_list; urlnode; urlnode = urlnode->next) {
383
384     int up; /* upload file counter within a single upload glob */
385     char *infiles; /* might be a glob pattern */
386     char *outfiles;
387     int infilenum;
388     URLGlob *inglob;
389
390     outfiles = NULL;
391     infilenum = 0;
392     inglob = NULL;
393
394     /* urlnode->url is the full URL (it might be NULL) */
395
396     if(!urlnode->url) {
397       /* This node has no URL. Free node data without destroying the
398          node itself nor modifying next pointer and continue to next */
399       Curl_safefree(urlnode->outfile);
400       Curl_safefree(urlnode->infile);
401       urlnode->flags = 0;
402       continue; /* next URL please */
403     }
404
405     /* save outfile pattern before expansion */
406     if(urlnode->outfile) {
407       outfiles = strdup(urlnode->outfile);
408       if(!outfiles) {
409         helpf(config->errors, "out of memory\n");
410         res = CURLE_OUT_OF_MEMORY;
411         break;
412       }
413     }
414
415     infiles = urlnode->infile;
416
417     if(!config->globoff && infiles) {
418       /* Unless explicitly shut off */
419       res = glob_url(&inglob, infiles, &infilenum,
420                      config->showerror?config->errors:NULL);
421       if(res) {
422         Curl_safefree(outfiles);
423         break;
424       }
425     }
426
427     /* Here's the loop for uploading multiple files within the same
428        single globbed string. If no upload, we enter the loop once anyway. */
429     for(up = 0 ;; up++) {
430
431       char *uploadfile; /* a single file, never a glob */
432       int separator;
433       URLGlob *urls;
434       int urlnum;
435
436       uploadfile = NULL;
437       separator = 0;
438       urls = NULL;
439       urlnum = 0;
440
441       if(!up && !infiles)
442         Curl_nop_stmt;
443       else {
444         if(inglob)
445           uploadfile = glob_next_url(inglob);
446         else if(!up)
447           uploadfile = strdup(infiles);
448         else
449           uploadfile = NULL;
450         if(!uploadfile)
451           break;
452       }
453
454       if(!config->globoff) {
455         /* Unless explicitly shut off, we expand '{...}' and '[...]'
456            expressions and return total number of URLs in pattern set */
457         res = glob_url(&urls, urlnode->url, &urlnum,
458                        config->showerror?config->errors:NULL);
459         if(res) {
460           Curl_safefree(uploadfile);
461           break;
462         }
463       }
464       else
465         urlnum = 1; /* without globbing, this is a single URL */
466
467       /* if multiple files extracted to stdout, insert separators! */
468       separator= ((!outfiles || curlx_strequal(outfiles, "-")) && urlnum > 1);
469
470       /* Here's looping around each globbed URL */
471       for(i = 0 ;; i++) {
472
473         int infd;
474         bool infdopen;
475         char *outfile;
476         struct OutStruct outs;
477         struct InStruct input;
478         struct timeval retrystart;
479         curl_off_t uploadfilesize;
480         long retry_numretries;
481         long retry_sleep_default;
482         long retry_sleep;
483         char *this_url;
484
485         outfile = NULL;
486         infdopen = FALSE;
487         infd = STDIN_FILENO;
488         uploadfilesize = -1; /* -1 means unknown */
489
490         /* default output stream is stdout */
491         memset(&outs, 0, sizeof(struct OutStruct));
492         outs.stream = stdout;
493         outs.config = config;
494
495         if(urls)
496           this_url = glob_next_url(urls);
497         else if(!i) {
498           this_url = strdup(urlnode->url);
499         }
500         else
501           this_url = NULL;
502         if(!this_url)
503           break;
504
505         if(outfiles) {
506           outfile = strdup(outfiles);
507           if(!outfile) {
508             res = CURLE_OUT_OF_MEMORY;
509             goto show_error;
510           }
511         }
512
513         if((urlnode->flags&GETOUT_USEREMOTE) ||
514            (outfile && !curlx_strequal("-", outfile)) ) {
515
516           /*
517            * We have specified a file name to store the result in, or we have
518            * decided we want to use the remote file name.
519            */
520
521           if(!outfile) {
522             /* extract the file name from the URL */
523             res = get_url_file_name(&outfile, this_url);
524             if(res)
525               goto show_error;
526             if((!outfile || !*outfile) && !config->content_disposition) {
527               helpf(config->errors, "Remote file name has no length!\n");
528               res = CURLE_WRITE_ERROR;
529               goto quit_urls;
530             }
531 #if defined(MSDOS) || defined(WIN32)
532             /* For DOS and WIN32, we do some major replacing of
533                bad characters in the file name before using it */
534             outfile = sanitize_dos_name(outfile);
535             if(!outfile) {
536               res = CURLE_OUT_OF_MEMORY;
537               goto show_error;
538             }
539 #endif /* MSDOS || WIN32 */
540           }
541           else if(urls) {
542             /* fill '#1' ... '#9' terms from URL pattern */
543             char *storefile = outfile;
544             outfile = glob_match_url(storefile, urls);
545             Curl_safefree(storefile);
546             if(!outfile) {
547               /* bad globbing */
548               warnf(config, "bad output glob!\n");
549               res = CURLE_FAILED_INIT;
550               goto quit_urls;
551             }
552           }
553
554           /* Create the directory hierarchy, if not pre-existent to a multiple
555              file output call */
556
557           if(config->create_dirs) {
558             res = create_dir_hierarchy(outfile, config->errors);
559             /* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
560             if(res == CURLE_WRITE_ERROR)
561               goto quit_urls;
562             if(res) {
563               goto show_error;
564             }
565           }
566
567           if((urlnode->flags & GETOUT_USEREMOTE)
568              && config->content_disposition) {
569             /* Our header callback sets the filename */
570             DEBUGASSERT(!outs.filename);
571           }
572           else {
573             if(config->resume_from_current) {
574               /* We're told to continue from where we are now. Get the size
575                  of the file as it is now and open it for append instead */
576               struct_stat fileinfo;
577               /* VMS -- Danger, the filesize is only valid for stream files */
578               if(0 == stat(outfile, &fileinfo))
579                 /* set offset to current file size: */
580                 config->resume_from = fileinfo.st_size;
581               else
582                 /* let offset be 0 */
583                 config->resume_from = 0;
584             }
585
586             if(config->resume_from) {
587               /* open file for output: */
588               FILE *file = fopen(outfile, config->resume_from?"ab":"wb");
589               if(!file) {
590                 helpf(config->errors, "Can't open '%s'!\n", outfile);
591                 res = CURLE_WRITE_ERROR;
592                 goto quit_urls;
593               }
594               outs.fopened = TRUE;
595               outs.stream = file;
596               outs.init = config->resume_from;
597             }
598             else {
599               outs.stream = NULL; /* open when needed */
600             }
601             outs.filename = outfile;
602             outs.s_isreg = TRUE;
603           }
604         }
605
606         if(uploadfile && !stdin_upload(uploadfile)) {
607           /*
608            * We have specified a file to upload and it isn't "-".
609            */
610           struct_stat fileinfo;
611
612           this_url = add_file_name_to_url(curl, this_url, uploadfile);
613           if(!this_url) {
614             res = CURLE_OUT_OF_MEMORY;
615             goto show_error;
616           }
617           /* VMS Note:
618            *
619            * Reading binary from files can be a problem...  Only FIXED, VAR
620            * etc WITHOUT implied CC will work Others need a \n appended to a
621            * line
622            *
623            * - Stat gives a size but this is UNRELIABLE in VMS As a f.e. a
624            * fixed file with implied CC needs to have a byte added for every
625            * record processed, this can by derived from Filesize & recordsize
626            * for VARiable record files the records need to be counted!  for
627            * every record add 1 for linefeed and subtract 2 for the record
628            * header for VARIABLE header files only the bare record data needs
629            * to be considered with one appended if implied CC
630            */
631
632           infd = open(uploadfile, O_RDONLY | O_BINARY);
633           if((infd == -1) || fstat(infd, &fileinfo)) {
634             helpf(config->errors, "Can't open '%s'!\n", uploadfile);
635             if(infd != -1) {
636               close(infd);
637               infd = STDIN_FILENO;
638             }
639             res = CURLE_READ_ERROR;
640             goto quit_urls;
641           }
642           infdopen = TRUE;
643
644           /* we ignore file size for char/block devices, sockets, etc. */
645           if(S_ISREG(fileinfo.st_mode))
646             uploadfilesize = fileinfo.st_size;
647
648         }
649         else if(uploadfile && stdin_upload(uploadfile)) {
650           /* count to see if there are more than one auth bit set
651              in the authtype field */
652           int authbits = 0;
653           int bitcheck = 0;
654           while(bitcheck < 32) {
655             if(config->authtype & (1 << bitcheck++)) {
656               authbits++;
657               if(authbits > 1) {
658                 /* more than one, we're done! */
659                 break;
660               }
661             }
662           }
663
664           /*
665            * If the user has also selected --anyauth or --proxy-anyauth
666            * we should warn him/her.
667            */
668           if(config->proxyanyauth || (authbits>1)) {
669             warnf(config,
670                   "Using --anyauth or --proxy-anyauth with upload from stdin"
671                   " involves a big risk of it not working. Use a temporary"
672                   " file or a fixed auth type instead!\n");
673           }
674
675           DEBUGASSERT(infdopen == FALSE);
676           DEBUGASSERT(infd == STDIN_FILENO);
677
678           set_binmode(stdin);
679           if(curlx_strequal(uploadfile, ".")) {
680             if(curlx_nonblock((curl_socket_t)infd, TRUE) < 0)
681               warnf(config,
682                     "fcntl failed on fd=%d: %s\n", infd, strerror(errno));
683           }
684         }
685
686         if(uploadfile && config->resume_from_current)
687           config->resume_from = -1; /* -1 will then force get-it-yourself */
688
689         if(output_expected(this_url, uploadfile)
690            && outs.stream && isatty(fileno(outs.stream)))
691           /* we send the output to a tty, therefore we switch off the progress
692              meter */
693           config->noprogress = config->isatty = TRUE;
694
695         if(urlnum > 1 && !(config->mute)) {
696           fprintf(config->errors, "\n[%d/%d]: %s --> %s\n",
697                   i+1, urlnum, this_url, outfile ? outfile : "<stdout>");
698           if(separator)
699             printf("%s%s\n", CURLseparator, this_url);
700         }
701         if(httpgetfields) {
702           char *urlbuffer;
703           /* Find out whether the url contains a file name */
704           const char *pc = strstr(this_url, "://");
705           char sep = '?';
706           if(pc)
707             pc += 3;
708           else
709             pc = this_url;
710
711           pc = strrchr(pc, '/'); /* check for a slash */
712
713           if(pc) {
714             /* there is a slash present in the URL */
715
716             if(strchr(pc, '?'))
717               /* Ouch, there's already a question mark in the URL string, we
718                  then append the data with an ampersand separator instead! */
719               sep='&';
720           }
721           /*
722            * Then append ? followed by the get fields to the url.
723            */
724           urlbuffer = malloc(strlen(this_url) + strlen(httpgetfields) + 3);
725           if(!urlbuffer) {
726             res = CURLE_OUT_OF_MEMORY;
727             goto show_error;
728           }
729           if(pc)
730             sprintf(urlbuffer, "%s%c%s", this_url, sep, httpgetfields);
731           else
732             /* Append  / before the ? to create a well-formed url
733                if the url contains a hostname only
734             */
735             sprintf(urlbuffer, "%s/?%s", this_url, httpgetfields);
736
737           Curl_safefree(this_url); /* free previous URL */
738           this_url = urlbuffer; /* use our new URL instead! */
739         }
740
741         if(!config->errors)
742           config->errors = stderr;
743
744         if((!outfile || !strcmp(outfile, "-")) && !config->use_ascii) {
745           /* We get the output to stdout and we have not got the ASCII/text
746              flag, then set stdout to be binary */
747           set_binmode(stdout);
748         }
749
750         if(config->tcp_nodelay)
751           my_setopt(curl, CURLOPT_TCP_NODELAY, 1);
752
753         /* where to store */
754         my_setopt(curl, CURLOPT_WRITEDATA, &outs);
755         /* what call to write */
756         my_setopt(curl, CURLOPT_WRITEFUNCTION, tool_write_cb);
757
758         /* for uploads */
759         input.fd = infd;
760         input.config = config;
761         my_setopt(curl, CURLOPT_READDATA, &input);
762         /* what call to read */
763         if((outfile && !curlx_strequal("-", outfile)) ||
764            !checkprefix("telnet:", this_url))
765           my_setopt(curl, CURLOPT_READFUNCTION, tool_read_cb);
766
767         /* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what
768            CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */
769         my_setopt(curl, CURLOPT_SEEKDATA, &input);
770         my_setopt(curl, CURLOPT_SEEKFUNCTION, tool_seek_cb);
771
772         if(config->recvpersecond)
773           /* tell libcurl to use a smaller sized buffer as it allows us to
774              make better sleeps! 7.9.9 stuff! */
775           my_setopt(curl, CURLOPT_BUFFERSIZE, config->recvpersecond);
776
777         /* size of uploaded file: */
778         if(uploadfilesize != -1)
779           my_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);
780         my_setopt_str(curl, CURLOPT_URL, this_url);     /* what to fetch */
781         my_setopt(curl, CURLOPT_NOPROGRESS, config->noprogress);
782         if(config->no_body) {
783           my_setopt(curl, CURLOPT_NOBODY, 1);
784           my_setopt(curl, CURLOPT_HEADER, 1);
785         }
786         else
787           my_setopt(curl, CURLOPT_HEADER, config->include_headers);
788
789 #if !defined(CURL_DISABLE_PROXY)
790         {
791           /* TODO: Make this a run-time check instead of compile-time one. */
792
793           my_setopt_str(curl, CURLOPT_PROXY, config->proxy);
794           my_setopt_str(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd);
795
796           /* new in libcurl 7.3 */
797           my_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel);
798
799           /* new in libcurl 7.5 */
800           if(config->proxy)
801             my_setopt(curl, CURLOPT_PROXYTYPE, config->proxyver);
802
803           /* new in libcurl 7.10 */
804           if(config->socksproxy) {
805             my_setopt_str(curl, CURLOPT_PROXY, config->socksproxy);
806             my_setopt(curl, CURLOPT_PROXYTYPE, config->socksver);
807           }
808
809           /* new in libcurl 7.10.6 */
810           if(config->proxyanyauth)
811             my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
812           else if(config->proxynegotiate)
813             my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_GSSNEGOTIATE);
814           else if(config->proxyntlm)
815             my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
816           else if(config->proxydigest)
817             my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
818           else if(config->proxybasic)
819             my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
820
821           /* new in libcurl 7.19.4 */
822           my_setopt(curl, CURLOPT_NOPROXY, config->noproxy);
823         }
824 #endif
825
826         my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror);
827         my_setopt(curl, CURLOPT_UPLOAD, uploadfile?TRUE:FALSE);
828         my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly);
829         my_setopt(curl, CURLOPT_APPEND, config->ftp_append);
830
831         if(config->netrc_opt)
832           my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
833         else if(config->netrc || config->netrc_file)
834           my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED);
835         else
836           my_setopt(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED);
837
838         if(config->netrc_file)
839           my_setopt(curl, CURLOPT_NETRC_FILE, config->netrc_file);
840
841         my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii);
842         my_setopt_str(curl, CURLOPT_USERPWD, config->userpwd);
843         my_setopt_str(curl, CURLOPT_RANGE, config->range);
844         my_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
845         my_setopt(curl, CURLOPT_TIMEOUT, config->timeout);
846
847         if(built_in_protos & CURLPROTO_HTTP) {
848
849           my_setopt(curl, CURLOPT_FOLLOWLOCATION,
850                     config->followlocation);
851           my_setopt(curl, CURLOPT_UNRESTRICTED_AUTH,
852                     config->unrestricted_auth);
853
854           switch(config->httpreq) {
855           case HTTPREQ_SIMPLEPOST:
856             my_setopt_str(curl, CURLOPT_POSTFIELDS,
857                           config->postfields);
858             my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE,
859                       config->postfieldsize);
860             break;
861           case HTTPREQ_POST:
862             my_setopt(curl, CURLOPT_HTTPPOST, config->httppost);
863             break;
864           default:
865             break;
866           }
867
868           my_setopt_str(curl, CURLOPT_REFERER, config->referer);
869           my_setopt(curl, CURLOPT_AUTOREFERER, config->autoreferer);
870           my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent);
871           my_setopt(curl, CURLOPT_HTTPHEADER, config->headers);
872
873           /* new in libcurl 7.5 */
874           my_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs);
875
876           /* new in libcurl 7.9.1 */
877           if(config->httpversion)
878             my_setopt(curl, CURLOPT_HTTP_VERSION, config->httpversion);
879
880           /* new in libcurl 7.10.6 (default is Basic) */
881           if(config->authtype)
882             my_setopt(curl, CURLOPT_HTTPAUTH, config->authtype);
883
884           /* curl 7.19.1 (the 301 version existed in 7.18.2) */
885           my_setopt(curl, CURLOPT_POSTREDIR, config->post301 |
886                     (config->post302 ? CURL_REDIR_POST_302 : FALSE));
887
888           /* new in libcurl 7.21.6 */
889           if(config->encoding)
890             my_setopt_str(curl, CURLOPT_ACCEPT_ENCODING, "");
891
892           /* new in libcurl 7.21.6 */
893           if(config->tr_encoding)
894             my_setopt(curl, CURLOPT_TRANSFER_ENCODING, 1);
895
896         } /* (built_in_protos & CURLPROTO_HTTP) */
897
898         my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport);
899         my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
900                   config->low_speed_limit);
901         my_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time);
902         my_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE,
903                   config->sendpersecond);
904         my_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE,
905                   config->recvpersecond);
906         my_setopt(curl, CURLOPT_RESUME_FROM_LARGE,
907                   config->use_resume?config->resume_from:0);
908
909         my_setopt(curl, CURLOPT_SSLCERT, config->cert);
910         my_setopt_str(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
911         my_setopt(curl, CURLOPT_SSLKEY, config->key);
912         my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type);
913         my_setopt_str(curl, CURLOPT_KEYPASSWD, config->key_passwd);
914
915         if(built_in_protos & (CURLPROTO_SCP|CURLPROTO_SFTP)) {
916
917           /* SSH and SSL private key uses same command-line option */
918           /* new in libcurl 7.16.1 */
919           my_setopt_str(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key);
920           /* new in libcurl 7.16.1 */
921           my_setopt_str(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey);
922
923           /* new in libcurl 7.17.1: SSH host key md5 checking allows us
924              to fail if we are not talking to who we think we should */
925           my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5,
926                         config->hostpubmd5);
927         }
928
929         if(config->cacert)
930           my_setopt_str(curl, CURLOPT_CAINFO, config->cacert);
931         if(config->capath)
932           my_setopt_str(curl, CURLOPT_CAPATH, config->capath);
933         if(config->crlfile)
934           my_setopt_str(curl, CURLOPT_CRLFILE, config->crlfile);
935
936         if(curlinfo->features & CURL_VERSION_SSL) {
937           if(config->insecure_ok) {
938             my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
939             my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
940           }
941           else {
942             my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
943             /* libcurl default is strict verifyhost -> 2L   */
944             /* my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); */
945           }
946         }
947
948         if(built_in_protos & (CURLPROTO_SCP|CURLPROTO_SFTP)) {
949           if(!config->insecure_ok) {
950             char *home;
951             char *file;
952             res = CURLE_OUT_OF_MEMORY;
953             home = homedir();
954             if(home) {
955               file = aprintf("%s/%sssh/known_hosts", home, DOT_CHAR);
956               if(file) {
957                 /* new in curl 7.19.6 */
958                 res = res_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, file);
959                 curl_free(file);
960                 if(res == CURLE_UNKNOWN_OPTION)
961                   /* libssh2 version older than 1.1.1 */
962                   res = CURLE_OK;
963               }
964               free(home);
965             }
966             if(res)
967               goto show_error;
968           }
969         }
970
971         if(config->no_body || config->remote_time) {
972           /* no body or use remote time */
973           my_setopt(curl, CURLOPT_FILETIME, TRUE);
974         }
975
976         my_setopt(curl, CURLOPT_CRLF, config->crlf);
977         my_setopt(curl, CURLOPT_QUOTE, config->quote);
978         my_setopt(curl, CURLOPT_POSTQUOTE, config->postquote);
979         my_setopt(curl, CURLOPT_PREQUOTE, config->prequote);
980
981 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
982         {
983           /* TODO: Make this a run-time check instead of compile-time one. */
984
985           if(config->cookie)
986             my_setopt_str(curl, CURLOPT_COOKIE, config->cookie);
987
988           if(config->cookiefile)
989             my_setopt_str(curl, CURLOPT_COOKIEFILE, config->cookiefile);
990
991           /* new in libcurl 7.9 */
992           if(config->cookiejar)
993             my_setopt_str(curl, CURLOPT_COOKIEJAR, config->cookiejar);
994
995           /* new in libcurl 7.9.7 */
996           my_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession);
997         }
998 #endif
999
1000         my_setopt(curl, CURLOPT_SSLVERSION, config->ssl_version);
1001         my_setopt(curl, CURLOPT_TIMECONDITION, config->timecond);
1002         my_setopt(curl, CURLOPT_TIMEVALUE, config->condtime);
1003         my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
1004         my_setopt(curl, CURLOPT_STDERR, config->errors);
1005
1006         /* three new ones in libcurl 7.3: */
1007         my_setopt_str(curl, CURLOPT_INTERFACE, config->iface);
1008         my_setopt_str(curl, CURLOPT_KRBLEVEL, config->krblevel);
1009
1010         progressbarinit(&progressbar, config);
1011         if((config->progressmode == CURL_PROGRESS_BAR) &&
1012            !config->noprogress && !config->mute) {
1013           /* we want the alternative style, then we have to implement it
1014              ourselves! */
1015           my_setopt(curl, CURLOPT_PROGRESSFUNCTION, tool_progress_cb);
1016           my_setopt(curl, CURLOPT_PROGRESSDATA, &progressbar);
1017         }
1018
1019         /* new in libcurl 7.6.2: */
1020         my_setopt(curl, CURLOPT_TELNETOPTIONS, config->telnet_options);
1021
1022         /* new in libcurl 7.7: */
1023         my_setopt_str(curl, CURLOPT_RANDOM_FILE, config->random_file);
1024         my_setopt(curl, CURLOPT_EGDSOCKET, config->egd_file);
1025         my_setopt(curl, CURLOPT_CONNECTTIMEOUT, config->connecttimeout);
1026
1027         if(config->cipher_list)
1028           my_setopt_str(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list);
1029
1030         /* new in libcurl 7.9.2: */
1031         if(config->disable_epsv)
1032           /* disable it */
1033           my_setopt(curl, CURLOPT_FTP_USE_EPSV, FALSE);
1034
1035         /* new in libcurl 7.10.5 */
1036         if(config->disable_eprt)
1037           /* disable it */
1038           my_setopt(curl, CURLOPT_FTP_USE_EPRT, FALSE);
1039
1040         if(config->tracetype != TRACE_NONE) {
1041           my_setopt(curl, CURLOPT_DEBUGFUNCTION, tool_debug_cb);
1042           my_setopt(curl, CURLOPT_DEBUGDATA, config);
1043           my_setopt(curl, CURLOPT_VERBOSE, TRUE);
1044         }
1045
1046         /* new in curl 7.9.3 */
1047         if(config->engine) {
1048           res = res_setopt_str(curl, CURLOPT_SSLENGINE, config->engine);
1049           if(res)
1050             goto show_error;
1051           my_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1);
1052         }
1053
1054         /* new in curl 7.10.7, extended in 7.19.4 but this only sets 0 or 1 */
1055         my_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS,
1056                   config->ftp_create_dirs);
1057
1058         /* new in curl 7.10.8 */
1059         if(config->max_filesize)
1060           my_setopt(curl, CURLOPT_MAXFILESIZE_LARGE,
1061                     config->max_filesize);
1062
1063         if(4 == config->ip_version)
1064           my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1065         else if(6 == config->ip_version)
1066           my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
1067         else
1068           my_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER);
1069
1070         /* new in curl 7.15.5 */
1071         if(config->ftp_ssl_reqd)
1072           my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
1073
1074         /* new in curl 7.11.0 */
1075         else if(config->ftp_ssl)
1076           my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
1077
1078         /* new in curl 7.16.0 */
1079         else if(config->ftp_ssl_control)
1080           my_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_CONTROL);
1081
1082         /* new in curl 7.16.1 */
1083         if(config->ftp_ssl_ccc)
1084           my_setopt(curl, CURLOPT_FTP_SSL_CCC, config->ftp_ssl_ccc_mode);
1085
1086 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1087         {
1088           /* TODO: Make this a run-time check instead of compile-time one. */
1089
1090           /* new in curl 7.19.4 */
1091           if(config->socks5_gssapi_service)
1092             my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_SERVICE,
1093                           config->socks5_gssapi_service);
1094
1095           /* new in curl 7.19.4 */
1096           if(config->socks5_gssapi_nec)
1097             my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_NEC,
1098                           config->socks5_gssapi_nec);
1099         }
1100 #endif
1101         /* curl 7.13.0 */
1102         my_setopt_str(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account);
1103
1104         my_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl);
1105
1106         /* curl 7.14.2 */
1107         my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip);
1108
1109         /* curl 7.15.1 */
1110         my_setopt(curl, CURLOPT_FTP_FILEMETHOD, config->ftp_filemethod);
1111
1112         /* curl 7.15.2 */
1113         if(config->localport) {
1114           my_setopt(curl, CURLOPT_LOCALPORT, config->localport);
1115           my_setopt_str(curl, CURLOPT_LOCALPORTRANGE,
1116                         config->localportrange);
1117         }
1118
1119         /* curl 7.15.5 */
1120         my_setopt_str(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER,
1121                       config->ftp_alternative_to_user);
1122
1123         /* curl 7.16.0 */
1124         if(config->disable_sessionid)
1125           my_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE,
1126                     !config->disable_sessionid);
1127
1128         /* curl 7.16.2 */
1129         if(config->raw) {
1130           my_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, FALSE);
1131           my_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, FALSE);
1132         }
1133
1134         /* curl 7.17.1 */
1135         if(!config->nokeepalive) {
1136           my_setopt(curl, CURLOPT_SOCKOPTFUNCTION, tool_sockopt_cb);
1137           my_setopt(curl, CURLOPT_SOCKOPTDATA, config);
1138         }
1139
1140         /* curl 7.20.0 */
1141         if(config->tftp_blksize)
1142           my_setopt(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize);
1143
1144         if(config->mail_from)
1145           my_setopt_str(curl, CURLOPT_MAIL_FROM, config->mail_from);
1146
1147         if(config->mail_rcpt)
1148           my_setopt(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt);
1149
1150         /* curl 7.20.x */
1151         if(config->ftp_pret)
1152           my_setopt(curl, CURLOPT_FTP_USE_PRET, TRUE);
1153
1154         if(config->proto_present)
1155           my_setopt(curl, CURLOPT_PROTOCOLS, config->proto);
1156         if(config->proto_redir_present)
1157           my_setopt(curl, CURLOPT_REDIR_PROTOCOLS, config->proto_redir);
1158
1159         if((urlnode->flags & GETOUT_USEREMOTE)
1160            && config->content_disposition) {
1161           my_setopt(curl, CURLOPT_HEADERFUNCTION, tool_header_cb);
1162           my_setopt(curl, CURLOPT_HEADERDATA, &outs);
1163         }
1164         else {
1165           /* if HEADERFUNCTION was set to something in the previous loop, it
1166              is important that we set it (back) to NULL now */
1167           my_setopt(curl, CURLOPT_HEADERFUNCTION, NULL);
1168           my_setopt(curl, CURLOPT_HEADERDATA, config->headerfile?&heads:NULL);
1169         }
1170
1171         if(config->resolve)
1172           /* new in 7.21.3 */
1173           my_setopt(curl, CURLOPT_RESOLVE, config->resolve);
1174
1175         /* new in 7.21.4 */
1176         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) {
1177           if(config->tls_username)
1178             my_setopt_str(curl, CURLOPT_TLSAUTH_USERNAME,
1179                           config->tls_username);
1180           if(config->tls_password)
1181             my_setopt_str(curl, CURLOPT_TLSAUTH_PASSWORD,
1182                           config->tls_password);
1183           if(config->tls_authtype)
1184             my_setopt_str(curl, CURLOPT_TLSAUTH_TYPE,
1185                           config->tls_authtype);
1186         }
1187
1188         /* new in 7.22.0 */
1189         if(config->gssapi_delegation)
1190           my_setopt_str(curl, CURLOPT_GSSAPI_DELEGATION,
1191                         config->gssapi_delegation);
1192
1193         /* initialize retry vars for loop below */
1194         retry_sleep_default = (config->retry_delay) ?
1195           config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */
1196
1197         retry_numretries = config->req_retry;
1198         retry_sleep = retry_sleep_default; /* ms */
1199         retrystart = cutil_tvnow();
1200
1201         for(;;) {
1202           res = curl_easy_perform(curl);
1203           if(!curl_slist_append(easysrc, "ret = curl_easy_perform(hnd);")) {
1204             res = CURLE_OUT_OF_MEMORY;
1205             goto show_error;
1206           }
1207
1208           if(config->content_disposition && outs.stream && !config->mute &&
1209              outs.filename)
1210             printf("curl: Saved to filename '%s'\n", outs.filename);
1211
1212           /* if retry-max-time is non-zero, make sure we haven't exceeded the
1213              time */
1214           if(retry_numretries &&
1215              (!config->retry_maxtime ||
1216               (cutil_tvdiff(cutil_tvnow(), retrystart)<
1217                config->retry_maxtime*1000L)) ) {
1218             enum {
1219               RETRY_NO,
1220               RETRY_TIMEOUT,
1221               RETRY_HTTP,
1222               RETRY_FTP,
1223               RETRY_LAST /* not used */
1224             } retry = RETRY_NO;
1225             long response;
1226             if(CURLE_OPERATION_TIMEDOUT == res)
1227               /* retry timeout always */
1228               retry = RETRY_TIMEOUT;
1229             else if((CURLE_OK == res) ||
1230                     (config->failonerror &&
1231                      (CURLE_HTTP_RETURNED_ERROR == res))) {
1232               /* If it returned OK. _or_ failonerror was enabled and it
1233                  returned due to such an error, check for HTTP transient
1234                  errors to retry on. */
1235               char *effective_url = NULL;
1236               curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effective_url);
1237               if(effective_url &&
1238                  checkprefix("http", effective_url)) {
1239                 /* This was HTTP(S) */
1240                 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
1241
1242                 switch(response) {
1243                 case 500: /* Internal Server Error */
1244                 case 502: /* Bad Gateway */
1245                 case 503: /* Service Unavailable */
1246                 case 504: /* Gateway Timeout */
1247                   retry = RETRY_HTTP;
1248                   /*
1249                    * At this point, we have already written data to the output
1250                    * file (or terminal). If we write to a file, we must rewind
1251                    * or close/re-open the file so that the next attempt starts
1252                    * over from the beginning.
1253                    *
1254                    * TODO: similar action for the upload case. We might need
1255                    * to start over reading from a previous point if we have
1256                    * uploaded something when this was returned.
1257                    */
1258                   break;
1259                 }
1260               }
1261             } /* if CURLE_OK */
1262             else if(res) {
1263               curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
1264
1265               if(response/100 == 4)
1266                 /*
1267                  * This is typically when the FTP server only allows a certain
1268                  * amount of users and we are not one of them.  All 4xx codes
1269                  * are transient.
1270                  */
1271                 retry = RETRY_FTP;
1272             }
1273
1274             if(retry) {
1275               static const char * const m[]={
1276                 NULL, "timeout", "HTTP error", "FTP error"
1277               };
1278               warnf(config, "Transient problem: %s "
1279                     "Will retry in %ld seconds. "
1280                     "%ld retries left.\n",
1281                     m[retry], retry_sleep/1000L, retry_numretries);
1282
1283               tool_go_sleep(retry_sleep);
1284               retry_numretries--;
1285               if(!config->retry_delay) {
1286                 retry_sleep *= 2;
1287                 if(retry_sleep > RETRY_SLEEP_MAX)
1288                   retry_sleep = RETRY_SLEEP_MAX;
1289               }
1290               if(outs.bytes && outs.filename) {
1291                 /* We have written data to a output file, we truncate file
1292                  */
1293                 if(!config->mute)
1294                   fprintf(config->errors, "Throwing away %"
1295                           CURL_FORMAT_CURL_OFF_T " bytes\n",
1296                           outs.bytes);
1297                 fflush(outs.stream);
1298                 /* truncate file at the position where we started appending */
1299 #ifdef HAVE_FTRUNCATE
1300                 if(ftruncate( fileno(outs.stream), outs.init)) {
1301                   /* when truncate fails, we can't just append as then we'll
1302                      create something strange, bail out */
1303                   if(!config->mute)
1304                     fprintf(config->errors,
1305                             "failed to truncate, exiting\n");
1306                   res = CURLE_WRITE_ERROR;
1307                   goto quit_urls;
1308                 }
1309                 /* now seek to the end of the file, the position where we
1310                    just truncated the file in a large file-safe way */
1311                 fseek(outs.stream, 0, SEEK_END);
1312 #else
1313                 /* ftruncate is not available, so just reposition the file
1314                    to the location we would have truncated it. This won't
1315                    work properly with large files on 32-bit systems, but
1316                    most of those will have ftruncate. */
1317                 fseek(outs.stream, (long)outs.init, SEEK_SET);
1318 #endif
1319                 outs.bytes = 0; /* clear for next round */
1320               }
1321               continue; /* curl_easy_perform loop */
1322             }
1323           } /* if retry_numretries */
1324
1325           /* In all ordinary cases, just break out of loop here */
1326           break; /* curl_easy_perform loop */
1327
1328         }
1329
1330         if((config->progressmode == CURL_PROGRESS_BAR) &&
1331            progressbar.calls)
1332           /* if the custom progress bar has been displayed, we output a
1333              newline here */
1334           fputs("\n", progressbar.out);
1335
1336         if(config->writeout)
1337           ourWriteOut(curl, config->writeout);
1338 #ifdef USE_ENVIRONMENT
1339         if(config->writeenv)
1340           ourWriteEnv(curl);
1341 #endif
1342
1343         /*
1344         ** Code within this loop may jump directly here to label 'show_error'
1345         ** in order to display an error message for CURLcode stored in 'res'
1346         ** variable and exit loop once that necessary writing and cleanup
1347         ** in label 'quit_urls' has been done.
1348         */
1349
1350         show_error:
1351
1352 #ifdef __VMS
1353         if(is_vms_shell()) {
1354           /* VMS DCL shell behavior */
1355           if(!config->showerror)
1356             vms_show = VMSSTS_HIDE;
1357         }
1358         else
1359 #endif
1360         if(res && config->showerror) {
1361           fprintf(config->errors, "curl: (%d) %s\n", res, (errorbuffer[0]) ?
1362                   errorbuffer : curl_easy_strerror((CURLcode)res));
1363           if(res == CURLE_SSL_CACERT)
1364             fprintf(config->errors, "%s%s",
1365                     CURL_CA_CERT_ERRORMSG1, CURL_CA_CERT_ERRORMSG2);
1366         }
1367
1368         /* Fall through comment to 'quit_urls' label */
1369
1370         /*
1371         ** Upon error condition and always that a message has already been
1372         ** displayed, code within this loop may jump directly here to label
1373         ** 'quit_urls' otherwise it should jump to 'show_error' label above.
1374         **
1375         ** When 'res' variable is _not_ CURLE_OK loop will exit once that
1376         ** all code following 'quit_urls' has been executed. Otherwise it
1377         ** will loop to the beginning from where it may exit if there are
1378         ** no more urls left.
1379         */
1380
1381         quit_urls:
1382
1383         /* Set file extended attributes */
1384         if(!res && config->xattr && outs.fopened && outs.stream) {
1385           int rc = fwrite_xattr(curl, fileno(outs.stream));
1386           if(rc)
1387             warnf(config, "Error setting extended attributes: %s\n",
1388                   strerror(errno));
1389         }
1390
1391         /* Close the file */
1392         if(outs.fopened && outs.stream) {
1393           int rc = fclose(outs.stream);
1394           if(!res && rc) {
1395             /* something went wrong in the writing process */
1396             res = CURLE_WRITE_ERROR;
1397             fprintf(config->errors, "(%d) Failed writing body\n", res);
1398           }
1399         }
1400         else if(!outs.s_isreg && outs.stream) {
1401           /* Dump standard stream buffered data */
1402           int rc = fflush(outs.stream);
1403           if(!res && rc) {
1404             /* something went wrong in the writing process */
1405             res = CURLE_WRITE_ERROR;
1406             fprintf(config->errors, "(%d) Failed writing body\n", res);
1407           }
1408         }
1409
1410 #ifdef __AMIGA__
1411         if(!res && outs.s_isreg && outs.filename) {
1412           /* Set the url (up to 80 chars) as comment for the file */
1413           if(strlen(url) > 78)
1414             url[79] = '\0';
1415           SetComment(outs.filename, url);
1416         }
1417 #endif
1418
1419 #ifdef HAVE_UTIME
1420         /* File time can only be set _after_ the file has been closed */
1421         if(!res && config->remote_time && outs.s_isreg && outs.filename) {
1422           /* Ask libcurl if we got a remote file time */
1423           long filetime = -1;
1424           curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
1425           if(filetime >= 0) {
1426             struct utimbuf times;
1427             times.actime = (time_t)filetime;
1428             times.modtime = (time_t)filetime;
1429             utime(outs.filename, &times); /* set the time we got */
1430           }
1431         }
1432 #endif
1433         /* No more business with this output struct */
1434         if(outs.alloc_filename)
1435           Curl_safefree(outs.filename);
1436         memset(&outs, 0, sizeof(struct OutStruct));
1437
1438         /* Free loop-local allocated memory and close loop-local opened fd */
1439
1440         Curl_safefree(outfile);
1441         Curl_safefree(this_url);
1442
1443         if(infdopen) {
1444           close(infd);
1445           infdopen = FALSE;
1446           infd = STDIN_FILENO;
1447         }
1448
1449         /* upon error exit loop */
1450         if(res)
1451           break;
1452
1453       } /* loop to the next URL */
1454
1455       /* Free loop-local allocated memory */
1456
1457       Curl_safefree(uploadfile);
1458
1459       if(urls) {
1460         /* Free list of remaining URLs */
1461         glob_cleanup(urls);
1462         urls = NULL;
1463       }
1464
1465       /* upon error exit loop */
1466       if(res)
1467         break;
1468
1469     } /* loop to the next globbed upload file */
1470
1471     /* Free loop-local allocated memory */
1472
1473     Curl_safefree(outfiles);
1474
1475     if(inglob) {
1476       /* Free list of globbed upload files */
1477       glob_cleanup(inglob);
1478       inglob = NULL;
1479     }
1480
1481     /* Free this URL node data without destroying the
1482        the node itself nor modifying next pointer. */
1483     Curl_safefree(urlnode->url);
1484     Curl_safefree(urlnode->outfile);
1485     Curl_safefree(urlnode->infile);
1486     urlnode->flags = 0;
1487
1488     /* TODO: Should CURLE_SSL_CACERT be included as critical error ? */
1489
1490     /*
1491     ** Bail out upon critical errors
1492     */
1493     switch(res) {
1494     case CURLE_FAILED_INIT:
1495     case CURLE_OUT_OF_MEMORY:
1496     case CURLE_FUNCTION_NOT_FOUND:
1497     case CURLE_BAD_FUNCTION_ARGUMENT:
1498       goto quit_curl;
1499     default:
1500       /* Merrily loop to next URL */
1501       break;
1502     }
1503
1504   } /* for-loop through all URLs */
1505
1506   /*
1507   ** Nested loops end here.
1508   */
1509
1510   quit_curl:
1511
1512   /* Free function-local referenced allocated memory */
1513   Curl_safefree(httpgetfields);
1514
1515   /* Free list of given URLs */
1516   clean_getout(config);
1517
1518   /* Cleanup the curl handle now that our
1519      progressbar struct is still in scope */
1520   if(curl) {
1521     curl_easy_cleanup(curl);
1522     config->easy = curl = NULL;
1523   }
1524   if(easysrc)
1525     curl_slist_append(easysrc, "curl_easy_cleanup(hnd);");
1526
1527   /* Close function-local opened file descriptors */
1528
1529   if(heads.fopened && heads.stream)
1530     fclose(heads.stream);
1531   if(heads.alloc_filename)
1532     Curl_safefree(heads.filename);
1533
1534   if(config->trace_fopened && config->trace_stream)
1535     fclose(config->trace_stream);
1536
1537   /* Dump the libcurl code if previously enabled.
1538      NOTE: that this function relies on config->errors amongst other things
1539      so not everything can be closed and cleaned before this is called */
1540   dumpeasysrc(config);
1541
1542   if(config->errors_fopened && config->errors)
1543     fclose(config->errors);
1544
1545   main_free(); /* cleanup */
1546
1547   return res;
1548 }
1549