• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 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 https://curl.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  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 #include "tool_setup.h"
25 
26 #ifdef HAVE_FCNTL_H
27 #  include <fcntl.h>
28 #endif
29 
30 #ifdef HAVE_LOCALE_H
31 #  include <locale.h>
32 #endif
33 
34 #ifdef HAVE_SYS_SELECT_H
35 #  include <sys/select.h>
36 #elif defined(HAVE_UNISTD_H)
37 #  include <unistd.h>
38 #endif
39 
40 #ifdef __VMS
41 #  include <fabdef.h>
42 #endif
43 
44 #ifdef __AMIGA__
45 #  include <proto/dos.h>
46 #endif
47 
48 #include "strcase.h"
49 
50 #define ENABLE_CURLX_PRINTF
51 /* use our own printf() functions */
52 #include "curlx.h"
53 
54 #include "tool_binmode.h"
55 #include "tool_cfgable.h"
56 #include "tool_cb_dbg.h"
57 #include "tool_cb_hdr.h"
58 #include "tool_cb_prg.h"
59 #include "tool_cb_rea.h"
60 #include "tool_cb_see.h"
61 #include "tool_cb_wrt.h"
62 #include "tool_dirhie.h"
63 #include "tool_doswin.h"
64 #include "tool_easysrc.h"
65 #include "tool_filetime.h"
66 #include "tool_getparam.h"
67 #include "tool_helpers.h"
68 #include "tool_findfile.h"
69 #include "tool_libinfo.h"
70 #include "tool_main.h"
71 #include "tool_msgs.h"
72 #include "tool_operate.h"
73 #include "tool_operhlp.h"
74 #include "tool_paramhlp.h"
75 #include "tool_parsecfg.h"
76 #include "tool_setopt.h"
77 #include "tool_sleep.h"
78 #include "tool_urlglob.h"
79 #include "tool_util.h"
80 #include "tool_writeout.h"
81 #include "tool_xattr.h"
82 #include "tool_vms.h"
83 #include "tool_help.h"
84 #include "tool_hugehelp.h"
85 #include "tool_progress.h"
86 #include "dynbuf.h"
87 
88 #include "memdebug.h" /* keep this as LAST include */
89 
90 #ifdef CURLDEBUG
91 /* libcurl's debug builds provide an extra function */
92 CURLcode curl_easy_perform_ev(CURL *easy);
93 #endif
94 
95 #ifndef O_BINARY
96 /* since O_BINARY as used in bitmasks, setting it to zero makes it usable in
97    source code but yet it doesn't ruin anything */
98 #  define O_BINARY 0
99 #endif
100 
101 #define CURL_CA_CERT_ERRORMSG                                               \
102   "More details here: https://curl.se/docs/sslcerts.html\n\n"          \
103   "curl failed to verify the legitimacy of the server and therefore "       \
104   "could not\nestablish a secure connection to it. To learn more about "    \
105   "this situation and\nhow to fix it, please visit the web page mentioned " \
106   "above.\n"
107 
108 static CURLcode single_transfer(struct GlobalConfig *global,
109                                 struct OperationConfig *config,
110                                 CURLSH *share,
111                                 bool capath_from_env,
112                                 bool *added);
113 static CURLcode create_transfer(struct GlobalConfig *global,
114                                 CURLSH *share,
115                                 bool *added);
116 
is_fatal_error(CURLcode code)117 static bool is_fatal_error(CURLcode code)
118 {
119   switch(code) {
120   case CURLE_FAILED_INIT:
121   case CURLE_OUT_OF_MEMORY:
122   case CURLE_UNKNOWN_OPTION:
123   case CURLE_FUNCTION_NOT_FOUND:
124   case CURLE_BAD_FUNCTION_ARGUMENT:
125     /* critical error */
126     return TRUE;
127   default:
128     break;
129   }
130 
131   /* no error or not critical */
132   return FALSE;
133 }
134 
135 /*
136  * Check if a given string is a PKCS#11 URI
137  */
is_pkcs11_uri(const char * string)138 static bool is_pkcs11_uri(const char *string)
139 {
140   if(curl_strnequal(string, "pkcs11:", 7)) {
141     return TRUE;
142   }
143   else {
144     return FALSE;
145   }
146 }
147 
148 #ifdef __VMS
149 /*
150  * get_vms_file_size does what it takes to get the real size of the file
151  *
152  * For fixed files, find out the size of the EOF block and adjust.
153  *
154  * For all others, have to read the entire file in, discarding the contents.
155  * Most posted text files will be small, and binary files like zlib archives
156  * and CD/DVD images should be either a STREAM_LF format or a fixed format.
157  *
158  */
vms_realfilesize(const char * name,const struct_stat * stat_buf)159 static curl_off_t vms_realfilesize(const char *name,
160                                    const struct_stat *stat_buf)
161 {
162   char buffer[8192];
163   curl_off_t count;
164   int ret_stat;
165   FILE * file;
166 
167   /* !checksrc! disable FOPENMODE 1 */
168   file = fopen(name, "r"); /* VMS */
169   if(!file) {
170     return 0;
171   }
172   count = 0;
173   ret_stat = 1;
174   while(ret_stat > 0) {
175     ret_stat = fread(buffer, 1, sizeof(buffer), file);
176     if(ret_stat)
177       count += ret_stat;
178   }
179   fclose(file);
180 
181   return count;
182 }
183 
184 /*
185  *
186  *  VmsSpecialSize checks to see if the stat st_size can be trusted and
187  *  if not to call a routine to get the correct size.
188  *
189  */
VmsSpecialSize(const char * name,const struct_stat * stat_buf)190 static curl_off_t VmsSpecialSize(const char *name,
191                                  const struct_stat *stat_buf)
192 {
193   switch(stat_buf->st_fab_rfm) {
194   case FAB$C_VAR:
195   case FAB$C_VFC:
196     return vms_realfilesize(name, stat_buf);
197     break;
198   default:
199     return stat_buf->st_size;
200   }
201 }
202 #endif /* __VMS */
203 
204 #define BUFFER_SIZE (100*1024)
205 
206 struct per_transfer *transfers; /* first node */
207 static struct per_transfer *transfersl; /* last node */
208 static long all_pers;
209 
210 /* add_per_transfer creates a new 'per_transfer' node in the linked
211    list of transfers */
add_per_transfer(struct per_transfer ** per)212 static CURLcode add_per_transfer(struct per_transfer **per)
213 {
214   struct per_transfer *p;
215   p = calloc(sizeof(struct per_transfer), 1);
216   if(!p)
217     return CURLE_OUT_OF_MEMORY;
218   if(!transfers)
219     /* first entry */
220     transfersl = transfers = p;
221   else {
222     /* make the last node point to the new node */
223     transfersl->next = p;
224     /* make the new node point back to the formerly last node */
225     p->prev = transfersl;
226     /* move the last node pointer to the new entry */
227     transfersl = p;
228   }
229   *per = p;
230   all_xfers++; /* count total number of transfers added */
231   all_pers++;
232 
233   return CURLE_OK;
234 }
235 
236 /* Remove the specified transfer from the list (and free it), return the next
237    in line */
del_per_transfer(struct per_transfer * per)238 static struct per_transfer *del_per_transfer(struct per_transfer *per)
239 {
240   struct per_transfer *n;
241   struct per_transfer *p;
242   DEBUGASSERT(transfers);
243   DEBUGASSERT(transfersl);
244   DEBUGASSERT(per);
245 
246   n = per->next;
247   p = per->prev;
248 
249   if(p)
250     p->next = n;
251   else
252     transfers = n;
253 
254   if(n)
255     n->prev = p;
256   else
257     transfersl = p;
258 
259   free(per);
260   all_pers--;
261 
262   return n;
263 }
264 
pre_transfer(struct GlobalConfig * global,struct per_transfer * per)265 static CURLcode pre_transfer(struct GlobalConfig *global,
266                              struct per_transfer *per)
267 {
268   curl_off_t uploadfilesize = -1;
269   struct_stat fileinfo;
270   CURLcode result = CURLE_OK;
271 
272   if(per->uploadfile && !stdin_upload(per->uploadfile)) {
273     /* VMS Note:
274      *
275      * Reading binary from files can be a problem...  Only FIXED, VAR
276      * etc WITHOUT implied CC will work. Others need a \n appended to
277      * a line
278      *
279      * - Stat gives a size but this is UNRELIABLE in VMS. E.g.
280      * a fixed file with implied CC needs to have a byte added for every
281      * record processed, this can be derived from Filesize & recordsize
282      * for VARiable record files the records need to be counted!  for
283      * every record add 1 for linefeed and subtract 2 for the record
284      * header for VARIABLE header files only the bare record data needs
285      * to be considered with one appended if implied CC
286      */
287 #ifdef __VMS
288     /* Calculate the real upload size for VMS */
289     per->infd = -1;
290     if(stat(per->uploadfile, &fileinfo) == 0) {
291       fileinfo.st_size = VmsSpecialSize(uploadfile, &fileinfo);
292       switch(fileinfo.st_fab_rfm) {
293       case FAB$C_VAR:
294       case FAB$C_VFC:
295       case FAB$C_STMCR:
296         per->infd = open(per->uploadfile, O_RDONLY | O_BINARY);
297         break;
298       default:
299         per->infd = open(per->uploadfile, O_RDONLY | O_BINARY,
300                         "rfm=stmlf", "ctx=stm");
301       }
302     }
303     if(per->infd == -1)
304 #else
305       per->infd = open(per->uploadfile, O_RDONLY | O_BINARY);
306     if((per->infd == -1) || fstat(per->infd, &fileinfo))
307 #endif
308     {
309       helpf(stderr, "Can't open '%s'!\n", per->uploadfile);
310       if(per->infd != -1) {
311         close(per->infd);
312         per->infd = STDIN_FILENO;
313       }
314       return CURLE_READ_ERROR;
315     }
316     per->infdopen = TRUE;
317 
318     /* we ignore file size for char/block devices, sockets, etc. */
319     if(S_ISREG(fileinfo.st_mode))
320       uploadfilesize = fileinfo.st_size;
321 
322     if(uploadfilesize != -1) {
323       struct OperationConfig *config = per->config; /* for the macro below */
324 #ifdef CURL_DISABLE_LIBCURL_OPTION
325       (void)config;
326       (void)global;
327 #endif
328       my_setopt(per->curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);
329     }
330     per->input.fd = per->infd;
331   }
332   per->start = tvnow();
333   return result;
334 }
335 
336 #ifdef __AMIGA__
AmigaSetComment(struct per_transfer * per,CURLcode result)337 static void AmigaSetComment(struct per_transfer *per,
338                             CURLcode result)
339 {
340   struct OutStruct *outs = &per->outs;
341   if(!result && outs->s_isreg && outs->filename) {
342     /* Set the url (up to 80 chars) as comment for the file */
343     if(strlen(per->this_url) > 78)
344       per->this_url[79] = '\0';
345     SetComment(outs->filename, per->this_url);
346   }
347 }
348 #else
349 #define AmigaSetComment(x,y) Curl_nop_stmt
350 #endif
351 
352 /* When doing serial transfers, we use a single fixed error area */
353 static char global_errorbuffer[CURL_ERROR_SIZE];
354 
single_transfer_cleanup(struct OperationConfig * config)355 void single_transfer_cleanup(struct OperationConfig *config)
356 {
357   if(config) {
358     struct State *state = &config->state;
359     if(state->urls) {
360       /* Free list of remaining URLs */
361       glob_cleanup(state->urls);
362       state->urls = NULL;
363     }
364     Curl_safefree(state->outfiles);
365     Curl_safefree(state->httpgetfields);
366     Curl_safefree(state->uploadfile);
367     if(state->inglob) {
368       /* Free list of globbed upload files */
369       glob_cleanup(state->inglob);
370       state->inglob = NULL;
371     }
372   }
373 }
374 
375 /*
376  * Call this after a transfer has completed.
377  */
post_per_transfer(struct GlobalConfig * global,struct per_transfer * per,CURLcode result,bool * retryp,long * delay)378 static CURLcode post_per_transfer(struct GlobalConfig *global,
379                                   struct per_transfer *per,
380                                   CURLcode result,
381                                   bool *retryp,
382                                   long *delay) /* milliseconds! */
383 {
384   struct OutStruct *outs = &per->outs;
385   CURL *curl = per->curl;
386   struct OperationConfig *config = per->config;
387 
388   if(!curl || !config)
389     return result;
390 
391   *retryp = FALSE;
392   *delay = 0; /* for no retry, keep it zero */
393 
394   if(per->infdopen)
395     close(per->infd);
396 
397 #ifdef __VMS
398   if(is_vms_shell()) {
399     /* VMS DCL shell behavior */
400     if(global->silent && !global->showerror)
401       vms_show = VMSSTS_HIDE;
402   }
403   else
404 #endif
405     if(!config->synthetic_error && result &&
406        (!global->silent || global->showerror)) {
407       const char *msg = per->errorbuffer;
408       fprintf(stderr, "curl: (%d) %s\n", result,
409               (msg && msg[0]) ? msg : curl_easy_strerror(result));
410       if(result == CURLE_PEER_FAILED_VERIFICATION)
411         fputs(CURL_CA_CERT_ERRORMSG, stderr);
412     }
413     else if(config->failwithbody) {
414       /* if HTTP response >= 400, return error */
415       long code = 0;
416       curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
417       if(code >= 400) {
418         if(!global->silent || global->showerror)
419           fprintf(stderr,
420                   "curl: (%d) The requested URL returned error: %ld\n",
421                   CURLE_HTTP_RETURNED_ERROR, code);
422         result = CURLE_HTTP_RETURNED_ERROR;
423       }
424     }
425   /* Set file extended attributes */
426   if(!result && config->xattr && outs->fopened && outs->stream) {
427     int rc = fwrite_xattr(curl, per->this_url, fileno(outs->stream));
428     if(rc)
429       warnf(config->global, "Error setting extended attributes on '%s': %s\n",
430             outs->filename, strerror(errno));
431   }
432 
433   if(!result && !outs->stream && !outs->bytes) {
434     /* we have received no data despite the transfer was successful
435        ==> force creation of an empty output file (if an output file
436        was specified) */
437     long cond_unmet = 0L;
438     /* do not create (or even overwrite) the file in case we get no
439        data because of unmet condition */
440     curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &cond_unmet);
441     if(!cond_unmet && !tool_create_output_file(outs, config))
442       result = CURLE_WRITE_ERROR;
443   }
444 
445   if(!outs->s_isreg && outs->stream) {
446     /* Dump standard stream buffered data */
447     int rc = fflush(outs->stream);
448     if(!result && rc) {
449       /* something went wrong in the writing process */
450       result = CURLE_WRITE_ERROR;
451       if(!global->silent || global->showerror)
452         fprintf(stderr, "curl: (%d) Failed writing body\n", result);
453     }
454   }
455 
456   /* if retry-max-time is non-zero, make sure we haven't exceeded the
457      time */
458   if(per->retry_numretries &&
459      (!config->retry_maxtime ||
460       (tvdiff(tvnow(), per->retrystart) <
461        config->retry_maxtime*1000L)) ) {
462     enum {
463       RETRY_NO,
464       RETRY_ALL_ERRORS,
465       RETRY_TIMEOUT,
466       RETRY_CONNREFUSED,
467       RETRY_HTTP,
468       RETRY_FTP,
469       RETRY_LAST /* not used */
470     } retry = RETRY_NO;
471     long response = 0;
472     if((CURLE_OPERATION_TIMEDOUT == result) ||
473        (CURLE_COULDNT_RESOLVE_HOST == result) ||
474        (CURLE_COULDNT_RESOLVE_PROXY == result) ||
475        (CURLE_FTP_ACCEPT_TIMEOUT == result))
476       /* retry timeout always */
477       retry = RETRY_TIMEOUT;
478     else if(config->retry_connrefused &&
479             (CURLE_COULDNT_CONNECT == result)) {
480       long oserrno = 0;
481       curl_easy_getinfo(curl, CURLINFO_OS_ERRNO, &oserrno);
482       if(ECONNREFUSED == oserrno)
483         retry = RETRY_CONNREFUSED;
484     }
485     else if((CURLE_OK == result) ||
486             ((config->failonerror || config->failwithbody) &&
487              (CURLE_HTTP_RETURNED_ERROR == result))) {
488       /* If it returned OK. _or_ failonerror was enabled and it
489          returned due to such an error, check for HTTP transient
490          errors to retry on. */
491       const char *scheme;
492       curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme);
493       scheme = proto_token(scheme);
494       if(scheme == proto_http || scheme == proto_https) {
495         /* This was HTTP(S) */
496         curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
497 
498         switch(response) {
499         case 408: /* Request Timeout */
500         case 429: /* Too Many Requests (RFC6585) */
501         case 500: /* Internal Server Error */
502         case 502: /* Bad Gateway */
503         case 503: /* Service Unavailable */
504         case 504: /* Gateway Timeout */
505           retry = RETRY_HTTP;
506           /*
507            * At this point, we have already written data to the output
508            * file (or terminal). If we write to a file, we must rewind
509            * or close/re-open the file so that the next attempt starts
510            * over from the beginning.
511            *
512            * TODO: similar action for the upload case. We might need
513            * to start over reading from a previous point if we have
514            * uploaded something when this was returned.
515            */
516           break;
517         }
518       }
519     } /* if CURLE_OK */
520     else if(result) {
521       const char *scheme;
522 
523       curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
524       curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme);
525       scheme = proto_token(scheme);
526 
527       if((scheme == proto_ftp || scheme == proto_ftps) && response / 100 == 4)
528         /*
529          * This is typically when the FTP server only allows a certain
530          * amount of users and we are not one of them.  All 4xx codes
531          * are transient.
532          */
533         retry = RETRY_FTP;
534     }
535 
536     if(result && !retry && config->retry_all_errors)
537       retry = RETRY_ALL_ERRORS;
538 
539     if(retry) {
540       long sleeptime = 0;
541       curl_off_t retry_after = 0;
542       static const char * const m[]={
543         NULL,
544         "(retrying all errors)",
545         ": timeout",
546         ": connection refused",
547         ": HTTP error",
548         ": FTP error"
549       };
550 
551       sleeptime = per->retry_sleep;
552       if(RETRY_HTTP == retry) {
553         curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &retry_after);
554         if(retry_after) {
555           /* store in a 'long', make sure it doesn't overflow */
556           if(retry_after > LONG_MAX/1000)
557             sleeptime = LONG_MAX;
558           else
559             sleeptime = (long)retry_after * 1000; /* milliseconds */
560 
561           /* if adding retry_after seconds to the process would exceed the
562              maximum time allowed for retrying, then exit the retries right
563              away */
564           if(config->retry_maxtime) {
565             curl_off_t seconds = tvdiff(tvnow(), per->retrystart)/1000;
566 
567             if((CURL_OFF_T_MAX - retry_after < seconds) ||
568                (seconds + retry_after > config->retry_maxtime)) {
569               warnf(config->global, "The Retry-After: time would "
570                     "make this command line exceed the maximum allowed time "
571                     "for retries.");
572               goto noretry;
573             }
574           }
575         }
576       }
577       warnf(config->global, "Problem %s. "
578             "Will retry in %ld seconds. "
579             "%ld retries left.\n",
580             m[retry], sleeptime/1000L, per->retry_numretries);
581 
582       per->retry_numretries--;
583       if(!config->retry_delay) {
584         per->retry_sleep *= 2;
585         if(per->retry_sleep > RETRY_SLEEP_MAX)
586           per->retry_sleep = RETRY_SLEEP_MAX;
587       }
588       if(outs->bytes && outs->filename && outs->stream) {
589         int rc;
590         /* We have written data to an output file, we truncate file
591          */
592         if(!global->silent)
593           fprintf(stderr, "Throwing away %"  CURL_FORMAT_CURL_OFF_T " bytes\n",
594                   outs->bytes);
595         fflush(outs->stream);
596         /* truncate file at the position where we started appending */
597 #ifdef HAVE_FTRUNCATE
598         if(ftruncate(fileno(outs->stream), outs->init)) {
599           /* when truncate fails, we can't just append as then we'll
600              create something strange, bail out */
601           if(!global->silent || global->showerror)
602             fprintf(stderr, "curl: (23) Failed to truncate file\n");
603           return CURLE_WRITE_ERROR;
604         }
605         /* now seek to the end of the file, the position where we
606            just truncated the file in a large file-safe way */
607         rc = fseek(outs->stream, 0, SEEK_END);
608 #else
609         /* ftruncate is not available, so just reposition the file
610            to the location we would have truncated it. This won't
611            work properly with large files on 32-bit systems, but
612            most of those will have ftruncate. */
613         rc = fseek(outs->stream, (long)outs->init, SEEK_SET);
614 #endif
615         if(rc) {
616           if(!global->silent || global->showerror)
617             fprintf(stderr, "curl: (23) Failed seeking to end of file\n");
618           return CURLE_WRITE_ERROR;
619         }
620         outs->bytes = 0; /* clear for next round */
621       }
622       *retryp = TRUE;
623       *delay = sleeptime;
624       return CURLE_OK;
625     }
626   } /* if retry_numretries */
627   noretry:
628 
629   if((global->progressmode == CURL_PROGRESS_BAR) &&
630      per->progressbar.calls)
631     /* if the custom progress bar has been displayed, we output a
632        newline here */
633     fputs("\n", per->progressbar.out);
634 
635   /* Close the outs file */
636   if(outs->fopened && outs->stream) {
637     int rc = fclose(outs->stream);
638     if(!result && rc) {
639       /* something went wrong in the writing process */
640       result = CURLE_WRITE_ERROR;
641       if(!global->silent || global->showerror)
642         fprintf(stderr, "curl: (%d) Failed writing body\n", result);
643     }
644     if(result && config->rm_partial) {
645       notef(global, "Removing output file: %s\n", outs->filename);
646       unlink(outs->filename);
647     }
648   }
649 
650   AmigaSetComment(per, result);
651 
652   /* File time can only be set _after_ the file has been closed */
653   if(!result && config->remote_time && outs->s_isreg && outs->filename) {
654     /* Ask libcurl if we got a remote file time */
655     curl_off_t filetime = -1;
656     curl_easy_getinfo(curl, CURLINFO_FILETIME_T, &filetime);
657     setfiletime(filetime, outs->filename, global);
658   }
659 
660   /* Write the --write-out data before cleanup but after result is final */
661   if(config->writeout)
662     ourWriteOut(config, per, result);
663 
664   /* Close function-local opened file descriptors */
665   if(per->heads.fopened && per->heads.stream)
666     fclose(per->heads.stream);
667 
668   if(per->heads.alloc_filename)
669     Curl_safefree(per->heads.filename);
670 
671   if(per->etag_save.fopened && per->etag_save.stream)
672     fclose(per->etag_save.stream);
673 
674   if(per->etag_save.alloc_filename)
675     Curl_safefree(per->etag_save.filename);
676 
677   curl_easy_cleanup(per->curl);
678   if(outs->alloc_filename)
679     free(outs->filename);
680   free(per->this_url);
681   free(per->outfile);
682   free(per->uploadfile);
683   if(global->parallel)
684     free(per->errorbuffer);
685 
686   return result;
687 }
688 
689 /*
690  * Return the protocol token for the scheme used in the given URL
691  */
url_proto(char * url)692 static const char *url_proto(char *url)
693 {
694   CURLU *uh = curl_url();
695   const char *proto = NULL;
696 
697   if(uh) {
698     if(url) {
699       if(!curl_url_set(uh, CURLUPART_URL, url,
700                        CURLU_GUESS_SCHEME | CURLU_NON_SUPPORT_SCHEME)) {
701         char *schemep = NULL;
702         if(!curl_url_get(uh, CURLUPART_SCHEME, &schemep,
703                          CURLU_DEFAULT_SCHEME) &&
704            schemep) {
705           proto = proto_token(schemep);
706           curl_free(schemep);
707         }
708       }
709     }
710     curl_url_cleanup(uh);
711   }
712   return proto? proto: "???";   /* Never match if not found. */
713 }
714 
715 /* create the next (singular) transfer */
single_transfer(struct GlobalConfig * global,struct OperationConfig * config,CURLSH * share,bool capath_from_env,bool * added)716 static CURLcode single_transfer(struct GlobalConfig *global,
717                                 struct OperationConfig *config,
718                                 CURLSH *share,
719                                 bool capath_from_env,
720                                 bool *added)
721 {
722   CURLcode result = CURLE_OK;
723   struct getout *urlnode;
724   bool orig_noprogress = global->noprogress;
725   bool orig_isatty = global->isatty;
726   struct State *state = &config->state;
727   char *httpgetfields = state->httpgetfields;
728   *added = FALSE; /* not yet */
729 
730   if(config->postfields) {
731     if(config->use_httpget) {
732       if(!httpgetfields) {
733         /* Use the postfields data for an HTTP get */
734         httpgetfields = state->httpgetfields = strdup(config->postfields);
735         Curl_safefree(config->postfields);
736         if(!httpgetfields) {
737           errorf(global, "out of memory\n");
738           result = CURLE_OUT_OF_MEMORY;
739         }
740         else if(SetHTTPrequest(config,
741                                (config->no_body?HTTPREQ_HEAD:HTTPREQ_GET),
742                                &config->httpreq)) {
743           result = CURLE_FAILED_INIT;
744         }
745       }
746     }
747     else {
748       if(SetHTTPrequest(config, HTTPREQ_SIMPLEPOST, &config->httpreq))
749         result = CURLE_FAILED_INIT;
750     }
751     if(result) {
752       single_transfer_cleanup(config);
753       return result;
754     }
755   }
756   if(!state->urlnode) {
757     /* first time caller, setup things */
758     state->urlnode = config->url_list;
759     state->infilenum = 1;
760   }
761 
762   while(config->state.urlnode) {
763     static bool warn_more_options = FALSE;
764     char *infiles; /* might be a glob pattern */
765     struct URLGlob *inglob = state->inglob;
766     urlnode = config->state.urlnode;
767 
768     /* urlnode->url is the full URL (it might be NULL) */
769 
770     if(!urlnode->url) {
771       /* This node has no URL. Free node data without destroying the
772          node itself nor modifying next pointer and continue to next */
773       Curl_safefree(urlnode->outfile);
774       Curl_safefree(urlnode->infile);
775       urlnode->flags = 0;
776       config->state.urlnode = urlnode->next;
777       state->up = 0;
778       if(!warn_more_options) {
779         /* only show this once */
780         warnf(config->global, "Got more output options than URLs\n");
781         warn_more_options = TRUE;
782       }
783       continue; /* next URL please */
784     }
785 
786     /* save outfile pattern before expansion */
787     if(urlnode->outfile && !state->outfiles) {
788       state->outfiles = strdup(urlnode->outfile);
789       if(!state->outfiles) {
790         errorf(global, "out of memory\n");
791         result = CURLE_OUT_OF_MEMORY;
792         break;
793       }
794     }
795 
796     infiles = urlnode->infile;
797 
798     if(!config->globoff && infiles && !inglob) {
799       /* Unless explicitly shut off */
800       result = glob_url(&inglob, infiles, &state->infilenum,
801                         (!global->silent || global->showerror)?
802                         stderr:NULL);
803       if(result)
804         break;
805       config->state.inglob = inglob;
806     }
807 
808     {
809       unsigned long urlnum;
810 
811       if(!state->up && !infiles)
812         Curl_nop_stmt;
813       else {
814         if(!state->uploadfile) {
815           if(inglob) {
816             result = glob_next_url(&state->uploadfile, inglob);
817             if(result == CURLE_OUT_OF_MEMORY)
818               errorf(global, "out of memory\n");
819           }
820           else if(!state->up) {
821             state->uploadfile = strdup(infiles);
822             if(!state->uploadfile) {
823               errorf(global, "out of memory\n");
824               result = CURLE_OUT_OF_MEMORY;
825             }
826           }
827         }
828         if(result)
829           break;
830       }
831 
832       if(!state->urlnum) {
833         if(!config->globoff) {
834           /* Unless explicitly shut off, we expand '{...}' and '[...]'
835              expressions and return total number of URLs in pattern set */
836           result = glob_url(&state->urls, urlnode->url, &state->urlnum,
837                             (!global->silent || global->showerror)?
838                             stderr:NULL);
839           if(result)
840             break;
841           urlnum = state->urlnum;
842         }
843         else
844           urlnum = 1; /* without globbing, this is a single URL */
845       }
846       else
847         urlnum = state->urlnum;
848 
849       if(state->up < state->infilenum) {
850         struct per_transfer *per = NULL;
851         struct OutStruct *outs;
852         struct InStruct *input;
853         struct OutStruct *heads;
854         struct OutStruct *etag_save;
855         struct HdrCbData *hdrcbdata = NULL;
856         struct OutStruct etag_first;
857         const char *use_proto;
858         CURL *curl;
859 
860         /* --etag-save */
861         memset(&etag_first, 0, sizeof(etag_first));
862         etag_save = &etag_first;
863         etag_save->stream = stdout;
864 
865         /* --etag-compare */
866         if(config->etag_compare_file) {
867           char *etag_from_file = NULL;
868           char *header = NULL;
869           ParameterError pe;
870 
871           /* open file for reading: */
872           FILE *file = fopen(config->etag_compare_file, FOPEN_READTEXT);
873           if(!file && !config->etag_save_file) {
874             errorf(global,
875                    "Failed to open %s\n", config->etag_compare_file);
876             result = CURLE_READ_ERROR;
877             break;
878           }
879 
880           if((PARAM_OK == file2string(&etag_from_file, file)) &&
881              etag_from_file) {
882             header = aprintf("If-None-Match: %s", etag_from_file);
883             Curl_safefree(etag_from_file);
884           }
885           else
886             header = aprintf("If-None-Match: \"\"");
887 
888           if(!header) {
889             if(file)
890               fclose(file);
891             errorf(global,
892                    "Failed to allocate memory for custom etag header\n");
893             result = CURLE_OUT_OF_MEMORY;
894             break;
895           }
896 
897           /* add Etag from file to list of custom headers */
898           pe = add2list(&config->headers, header);
899           Curl_safefree(header);
900 
901           if(file)
902             fclose(file);
903           if(pe != PARAM_OK) {
904             result = CURLE_OUT_OF_MEMORY;
905             break;
906           }
907         }
908 
909         if(config->etag_save_file) {
910           /* open file for output: */
911           if(strcmp(config->etag_save_file, "-")) {
912             FILE *newfile = fopen(config->etag_save_file, "wb");
913             if(!newfile) {
914               warnf(global, "Failed creating file for saving etags: \"%s\". "
915                     "Skip this transfer\n", config->etag_save_file);
916               Curl_safefree(state->outfiles);
917               glob_cleanup(state->urls);
918               return CURLE_OK;
919             }
920             else {
921               etag_save->filename = config->etag_save_file;
922               etag_save->s_isreg = TRUE;
923               etag_save->fopened = TRUE;
924               etag_save->stream = newfile;
925             }
926           }
927           else {
928             /* always use binary mode for protocol header output */
929             set_binmode(etag_save->stream);
930           }
931         }
932 
933         curl = curl_easy_init();
934         if(curl)
935           result = add_per_transfer(&per);
936         else
937           result = CURLE_OUT_OF_MEMORY;
938         if(result) {
939           curl_easy_cleanup(curl);
940           if(etag_save->fopened)
941             fclose(etag_save->stream);
942           break;
943         }
944         per->etag_save = etag_first; /* copy the whole struct */
945         if(state->uploadfile) {
946           per->uploadfile = strdup(state->uploadfile);
947           if(!per->uploadfile) {
948             curl_easy_cleanup(curl);
949             result = CURLE_OUT_OF_MEMORY;
950             break;
951           }
952           if(SetHTTPrequest(config, HTTPREQ_PUT, &config->httpreq)) {
953             Curl_safefree(per->uploadfile);
954             curl_easy_cleanup(curl);
955             result = CURLE_FAILED_INIT;
956             break;
957           }
958         }
959         *added = TRUE;
960         per->config = config;
961         per->curl = curl;
962         per->urlnum = urlnode->num;
963 
964         /* default headers output stream is stdout */
965         heads = &per->heads;
966         heads->stream = stdout;
967 
968         /* Single header file for all URLs */
969         if(config->headerfile) {
970           /* open file for output: */
971           if(strcmp(config->headerfile, "-")) {
972             FILE *newfile;
973 
974             /*
975              * Since every transfer has its own file handle for dumping
976              * the headers, we need to open it in append mode, since transfers
977              * might finish in any order.
978              * The first transfer just clears the file.
979              * TODO: Consider placing the file handle inside the
980              * OperationConfig, so that it does not need to be opened/closed
981              * for every transfer.
982              */
983             if(!per->prev || per->prev->config != config) {
984               newfile = fopen(config->headerfile, "wb");
985               if(newfile)
986                 fclose(newfile);
987             }
988             newfile = fopen(config->headerfile, "ab");
989 
990             if(!newfile) {
991               errorf(global, "Failed to open %s\n", config->headerfile);
992               result = CURLE_WRITE_ERROR;
993               break;
994             }
995             else {
996               heads->filename = config->headerfile;
997               heads->s_isreg = TRUE;
998               heads->fopened = TRUE;
999               heads->stream = newfile;
1000             }
1001           }
1002           else {
1003             /* always use binary mode for protocol header output */
1004             set_binmode(heads->stream);
1005           }
1006         }
1007 
1008         hdrcbdata = &per->hdrcbdata;
1009 
1010         outs = &per->outs;
1011         input = &per->input;
1012 
1013         per->outfile = NULL;
1014         per->infdopen = FALSE;
1015         per->infd = STDIN_FILENO;
1016 
1017         /* default output stream is stdout */
1018         outs->stream = stdout;
1019 
1020         if(state->urls) {
1021           result = glob_next_url(&per->this_url, state->urls);
1022           if(result)
1023             break;
1024         }
1025         else if(!state->li) {
1026           per->this_url = strdup(urlnode->url);
1027           if(!per->this_url) {
1028             result = CURLE_OUT_OF_MEMORY;
1029             break;
1030           }
1031         }
1032         else
1033           per->this_url = NULL;
1034         if(!per->this_url)
1035           break;
1036 
1037         if(state->outfiles) {
1038           per->outfile = strdup(state->outfiles);
1039           if(!per->outfile) {
1040             result = CURLE_OUT_OF_MEMORY;
1041             break;
1042           }
1043         }
1044 
1045         if(((urlnode->flags&GETOUT_USEREMOTE) ||
1046             (per->outfile && strcmp("-", per->outfile)))) {
1047 
1048           /*
1049            * We have specified a file name to store the result in, or we have
1050            * decided we want to use the remote file name.
1051            */
1052 
1053           if(!per->outfile) {
1054             /* extract the file name from the URL */
1055             result = get_url_file_name(&per->outfile, per->this_url);
1056             if(result) {
1057               errorf(global, "Failed to extract a sensible file name"
1058                      " from the URL to use for storage!\n");
1059               break;
1060             }
1061             if(!*per->outfile && !config->content_disposition) {
1062               errorf(global, "Remote file name has no length!\n");
1063               result = CURLE_WRITE_ERROR;
1064               break;
1065             }
1066           }
1067           else if(state->urls) {
1068             /* fill '#1' ... '#9' terms from URL pattern */
1069             char *storefile = per->outfile;
1070             result = glob_match_url(&per->outfile, storefile, state->urls);
1071             Curl_safefree(storefile);
1072             if(result) {
1073               /* bad globbing */
1074               warnf(global, "bad output glob!\n");
1075               break;
1076             }
1077             if(!*per->outfile) {
1078               warnf(global, "output glob produces empty string!\n");
1079               result = CURLE_WRITE_ERROR;
1080               break;
1081             }
1082           }
1083 
1084           if(config->output_dir && *config->output_dir) {
1085             char *d = aprintf("%s/%s", config->output_dir, per->outfile);
1086             if(!d) {
1087               result = CURLE_WRITE_ERROR;
1088               break;
1089             }
1090             free(per->outfile);
1091             per->outfile = d;
1092           }
1093           /* Create the directory hierarchy, if not pre-existent to a multiple
1094              file output call */
1095 
1096           if(config->create_dirs) {
1097             result = create_dir_hierarchy(per->outfile, stderr);
1098             /* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
1099             if(result)
1100               break;
1101           }
1102 
1103           if((urlnode->flags & GETOUT_USEREMOTE)
1104              && config->content_disposition) {
1105             /* Our header callback MIGHT set the filename */
1106             DEBUGASSERT(!outs->filename);
1107           }
1108 
1109           if(config->resume_from_current) {
1110             /* We're told to continue from where we are now. Get the size
1111                of the file as it is now and open it for append instead */
1112             struct_stat fileinfo;
1113             /* VMS -- Danger, the filesize is only valid for stream files */
1114             if(0 == stat(per->outfile, &fileinfo))
1115               /* set offset to current file size: */
1116               config->resume_from = fileinfo.st_size;
1117             else
1118               /* let offset be 0 */
1119               config->resume_from = 0;
1120           }
1121 
1122           if(config->resume_from) {
1123 #ifdef __VMS
1124             /* open file for output, forcing VMS output format into stream
1125                mode which is needed for stat() call above to always work. */
1126             FILE *file = fopen(outfile, "ab",
1127                                "ctx=stm", "rfm=stmlf", "rat=cr", "mrs=0");
1128 #else
1129             /* open file for output: */
1130             FILE *file = fopen(per->outfile, "ab");
1131 #endif
1132             if(!file) {
1133               errorf(global, "Can't open '%s'!\n", per->outfile);
1134               result = CURLE_WRITE_ERROR;
1135               break;
1136             }
1137             outs->fopened = TRUE;
1138             outs->stream = file;
1139             outs->init = config->resume_from;
1140           }
1141           else {
1142             outs->stream = NULL; /* open when needed */
1143           }
1144           outs->filename = per->outfile;
1145           outs->s_isreg = TRUE;
1146         }
1147 
1148         if(per->uploadfile && !stdin_upload(per->uploadfile)) {
1149           /*
1150            * We have specified a file to upload and it isn't "-".
1151            */
1152           result = add_file_name_to_url(per->curl, &per->this_url,
1153                                         per->uploadfile);
1154           if(result)
1155             break;
1156         }
1157         else if(per->uploadfile && stdin_upload(per->uploadfile)) {
1158           /* count to see if there are more than one auth bit set
1159              in the authtype field */
1160           int authbits = 0;
1161           int bitcheck = 0;
1162           while(bitcheck < 32) {
1163             if(config->authtype & (1UL << bitcheck++)) {
1164               authbits++;
1165               if(authbits > 1) {
1166                 /* more than one, we're done! */
1167                 break;
1168               }
1169             }
1170           }
1171 
1172           /*
1173            * If the user has also selected --anyauth or --proxy-anyauth
1174            * we should warn them.
1175            */
1176           if(config->proxyanyauth || (authbits>1)) {
1177             warnf(global,
1178                   "Using --anyauth or --proxy-anyauth with upload from stdin"
1179                   " involves a big risk of it not working. Use a temporary"
1180                   " file or a fixed auth type instead!\n");
1181           }
1182 
1183           DEBUGASSERT(per->infdopen == FALSE);
1184           DEBUGASSERT(per->infd == STDIN_FILENO);
1185 
1186           set_binmode(stdin);
1187           if(!strcmp(per->uploadfile, ".")) {
1188             if(curlx_nonblock((curl_socket_t)per->infd, TRUE) < 0)
1189               warnf(global,
1190                     "fcntl failed on fd=%d: %s\n", per->infd, strerror(errno));
1191           }
1192         }
1193 
1194         if(per->uploadfile && config->resume_from_current)
1195           config->resume_from = -1; /* -1 will then force get-it-yourself */
1196 
1197         if(output_expected(per->this_url, per->uploadfile) && outs->stream &&
1198            isatty(fileno(outs->stream)))
1199           /* we send the output to a tty, therefore we switch off the progress
1200              meter */
1201           per->noprogress = global->noprogress = global->isatty = TRUE;
1202         else {
1203           /* progress meter is per download, so restore config
1204              values */
1205           per->noprogress = global->noprogress = orig_noprogress;
1206           global->isatty = orig_isatty;
1207         }
1208 
1209         if(httpgetfields || config->query) {
1210           char *q = httpgetfields ? httpgetfields : config->query;
1211           CURLU *uh = curl_url();
1212           if(uh) {
1213             CURLUcode uerr;
1214             uerr = curl_url_set(uh, CURLUPART_URL, per->this_url,
1215                             CURLU_GUESS_SCHEME);
1216             if(uerr) {
1217               result = urlerr_cvt(uerr);
1218               errorf(global, "(%d) Could not parse the URL, "
1219                      "failed to set query\n", result);
1220               config->synthetic_error = TRUE;
1221             }
1222             else {
1223               char *updated = NULL;
1224               uerr = curl_url_set(uh, CURLUPART_QUERY, q, CURLU_APPENDQUERY);
1225               if(!uerr)
1226                 uerr = curl_url_get(uh, CURLUPART_URL, &updated,
1227                                    CURLU_GUESS_SCHEME);
1228               if(uerr)
1229                 result = urlerr_cvt(uerr);
1230               else {
1231                 Curl_safefree(per->this_url); /* free previous URL */
1232                 per->this_url = updated; /* use our new URL instead! */
1233               }
1234             }
1235             curl_url_cleanup(uh);
1236             if(result)
1237               break;
1238           }
1239         }
1240 
1241         if((!per->outfile || !strcmp(per->outfile, "-")) &&
1242            !config->use_ascii) {
1243           /* We get the output to stdout and we have not got the ASCII/text
1244              flag, then set stdout to be binary */
1245           set_binmode(stdout);
1246         }
1247 
1248         /* explicitly passed to stdout means okaying binary gunk */
1249         config->terminal_binary_ok =
1250           (per->outfile && !strcmp(per->outfile, "-"));
1251 
1252         /* Avoid having this setopt added to the --libcurl source output. */
1253         result = curl_easy_setopt(curl, CURLOPT_SHARE, share);
1254         if(result)
1255           break;
1256 
1257         use_proto = url_proto(per->this_url);
1258 
1259         /* On most modern OSes, exiting works thoroughly,
1260            we'll clean everything up via exit(), so don't bother with
1261            slow cleanups. Crappy ones might need to skip this.
1262            Note: avoid having this setopt added to the --libcurl source
1263            output. */
1264         result = curl_easy_setopt(curl, CURLOPT_QUICK_EXIT, 1L);
1265         if(result)
1266           break;
1267 
1268         if(!config->tcp_nodelay)
1269           my_setopt(curl, CURLOPT_TCP_NODELAY, 0L);
1270 
1271         if(config->tcp_fastopen)
1272           my_setopt(curl, CURLOPT_TCP_FASTOPEN, 1L);
1273 
1274         /* where to store */
1275         my_setopt(curl, CURLOPT_WRITEDATA, per);
1276         my_setopt(curl, CURLOPT_INTERLEAVEDATA, per);
1277 
1278         /* what call to write */
1279         my_setopt(curl, CURLOPT_WRITEFUNCTION, tool_write_cb);
1280 
1281         /* for uploads */
1282         input->config = config;
1283         input->per = per;
1284         /* Note that if CURLOPT_READFUNCTION is fread (the default), then
1285          * lib/telnet.c will Curl_poll() on the input file descriptor
1286          * rather than calling the READFUNCTION at regular intervals.
1287          * The circumstances in which it is preferable to enable this
1288          * behavior, by omitting to set the READFUNCTION & READDATA options,
1289          * have not been determined.
1290          */
1291         my_setopt(curl, CURLOPT_READDATA, input);
1292         /* what call to read */
1293         my_setopt(curl, CURLOPT_READFUNCTION, tool_read_cb);
1294 
1295         /* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what
1296            CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */
1297         my_setopt(curl, CURLOPT_SEEKDATA, input);
1298         my_setopt(curl, CURLOPT_SEEKFUNCTION, tool_seek_cb);
1299 
1300         {
1301 #ifdef CURLDEBUG
1302           char *env = getenv("CURL_BUFFERSIZE");
1303           if(env) {
1304             long size = strtol(env, NULL, 10);
1305             if(size)
1306               my_setopt(curl, CURLOPT_BUFFERSIZE, size);
1307           }
1308           else
1309 #endif
1310           if(config->recvpersecond &&
1311              (config->recvpersecond < BUFFER_SIZE))
1312             /* use a smaller sized buffer for better sleeps */
1313             my_setopt(curl, CURLOPT_BUFFERSIZE, (long)config->recvpersecond);
1314           else
1315             my_setopt(curl, CURLOPT_BUFFERSIZE, (long)BUFFER_SIZE);
1316         }
1317 
1318         my_setopt_str(curl, CURLOPT_URL, per->this_url);
1319         my_setopt(curl, CURLOPT_NOPROGRESS,
1320                   global->noprogress || global->silent?1L:0L);
1321         if(config->no_body)
1322           my_setopt(curl, CURLOPT_NOBODY, 1L);
1323 
1324         if(config->oauth_bearer)
1325           my_setopt_str(curl, CURLOPT_XOAUTH2_BEARER, config->oauth_bearer);
1326 
1327         my_setopt_str(curl, CURLOPT_PROXY, config->proxy);
1328 
1329         if(config->proxy && result) {
1330           errorf(global, "proxy support is disabled in this libcurl\n");
1331           config->synthetic_error = TRUE;
1332           result = CURLE_NOT_BUILT_IN;
1333           break;
1334         }
1335 
1336         /* new in libcurl 7.5 */
1337         if(config->proxy)
1338           my_setopt_enum(curl, CURLOPT_PROXYTYPE, config->proxyver);
1339 
1340         my_setopt_str(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd);
1341 
1342         /* new in libcurl 7.3 */
1343         my_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel?1L:0L);
1344 
1345         /* new in libcurl 7.52.0 */
1346         if(config->preproxy)
1347           my_setopt_str(curl, CURLOPT_PRE_PROXY, config->preproxy);
1348 
1349         /* new in libcurl 7.10.6 */
1350         if(config->proxyanyauth)
1351           my_setopt_bitmask(curl, CURLOPT_PROXYAUTH,
1352                             (long)CURLAUTH_ANY);
1353         else if(config->proxynegotiate)
1354           my_setopt_bitmask(curl, CURLOPT_PROXYAUTH,
1355                             (long)CURLAUTH_GSSNEGOTIATE);
1356         else if(config->proxyntlm)
1357           my_setopt_bitmask(curl, CURLOPT_PROXYAUTH,
1358                             (long)CURLAUTH_NTLM);
1359         else if(config->proxydigest)
1360           my_setopt_bitmask(curl, CURLOPT_PROXYAUTH,
1361                             (long)CURLAUTH_DIGEST);
1362         else if(config->proxybasic)
1363           my_setopt_bitmask(curl, CURLOPT_PROXYAUTH,
1364                             (long)CURLAUTH_BASIC);
1365 
1366         /* new in libcurl 7.19.4 */
1367         my_setopt_str(curl, CURLOPT_NOPROXY, config->noproxy);
1368 
1369         my_setopt(curl, CURLOPT_SUPPRESS_CONNECT_HEADERS,
1370                   config->suppress_connect_headers?1L:0L);
1371 
1372         my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror?1L:0L);
1373         my_setopt(curl, CURLOPT_REQUEST_TARGET, config->request_target);
1374         my_setopt(curl, CURLOPT_UPLOAD, per->uploadfile?1L:0L);
1375         my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly?1L:0L);
1376         my_setopt(curl, CURLOPT_APPEND, config->ftp_append?1L:0L);
1377 
1378         if(config->netrc_opt)
1379           my_setopt_enum(curl, CURLOPT_NETRC, (long)CURL_NETRC_OPTIONAL);
1380         else if(config->netrc || config->netrc_file)
1381           my_setopt_enum(curl, CURLOPT_NETRC, (long)CURL_NETRC_REQUIRED);
1382         else
1383           my_setopt_enum(curl, CURLOPT_NETRC, (long)CURL_NETRC_IGNORED);
1384 
1385         if(config->netrc_file)
1386           my_setopt_str(curl, CURLOPT_NETRC_FILE, config->netrc_file);
1387 
1388         my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii?1L:0L);
1389         if(config->login_options)
1390           my_setopt_str(curl, CURLOPT_LOGIN_OPTIONS, config->login_options);
1391         my_setopt_str(curl, CURLOPT_USERPWD, config->userpwd);
1392         my_setopt_str(curl, CURLOPT_RANGE, config->range);
1393         if(!global->parallel) {
1394           per->errorbuffer = global_errorbuffer;
1395           my_setopt(curl, CURLOPT_ERRORBUFFER, global_errorbuffer);
1396         }
1397         my_setopt(curl, CURLOPT_TIMEOUT_MS, config->timeout_ms);
1398 
1399         switch(config->httpreq) {
1400         case HTTPREQ_SIMPLEPOST:
1401           my_setopt_str(curl, CURLOPT_POSTFIELDS,
1402                         config->postfields);
1403           my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE,
1404                     config->postfieldsize);
1405           break;
1406         case HTTPREQ_MIMEPOST:
1407           /* free previous remainders */
1408           curl_mime_free(config->mimepost);
1409           config->mimepost = NULL;
1410           result = tool2curlmime(curl, config->mimeroot, &config->mimepost);
1411           if(result)
1412             break;
1413           my_setopt_mimepost(curl, CURLOPT_MIMEPOST, config->mimepost);
1414           break;
1415         default:
1416           break;
1417         }
1418         if(result)
1419           break;
1420 
1421         /* new in libcurl 7.81.0 */
1422         if(config->mime_options)
1423           my_setopt(curl, CURLOPT_MIME_OPTIONS, config->mime_options);
1424 
1425         /* new in libcurl 7.10.6 (default is Basic) */
1426         if(config->authtype)
1427           my_setopt_bitmask(curl, CURLOPT_HTTPAUTH, (long)config->authtype);
1428 
1429         my_setopt_slist(curl, CURLOPT_HTTPHEADER, config->headers);
1430 
1431         if(proto_http || proto_rtsp) {
1432           my_setopt_str(curl, CURLOPT_REFERER, config->referer);
1433           my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent);
1434         }
1435 
1436         if(proto_http) {
1437           long postRedir = 0;
1438 
1439           my_setopt(curl, CURLOPT_FOLLOWLOCATION,
1440                     config->followlocation?1L:0L);
1441           my_setopt(curl, CURLOPT_UNRESTRICTED_AUTH,
1442                     config->unrestricted_auth?1L:0L);
1443 
1444           my_setopt(curl, CURLOPT_AUTOREFERER, config->autoreferer?1L:0L);
1445 
1446           /* new in libcurl 7.36.0 */
1447           if(config->proxyheaders) {
1448             my_setopt_slist(curl, CURLOPT_PROXYHEADER, config->proxyheaders);
1449             my_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE);
1450           }
1451 
1452           /* new in libcurl 7.5 */
1453           my_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs);
1454 
1455           if(config->httpversion)
1456             my_setopt_enum(curl, CURLOPT_HTTP_VERSION, config->httpversion);
1457           else if(feature_http2)
1458             my_setopt_enum(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
1459 
1460           /* curl 7.19.1 (the 301 version existed in 7.18.2),
1461              303 was added in 7.26.0 */
1462           if(config->post301)
1463             postRedir |= CURL_REDIR_POST_301;
1464           if(config->post302)
1465             postRedir |= CURL_REDIR_POST_302;
1466           if(config->post303)
1467             postRedir |= CURL_REDIR_POST_303;
1468           my_setopt(curl, CURLOPT_POSTREDIR, postRedir);
1469 
1470           /* new in libcurl 7.21.6 */
1471           if(config->encoding)
1472             my_setopt_str(curl, CURLOPT_ACCEPT_ENCODING, "");
1473 
1474           /* new in libcurl 7.21.6 */
1475           if(config->tr_encoding)
1476             my_setopt(curl, CURLOPT_TRANSFER_ENCODING, 1L);
1477           /* new in libcurl 7.64.0 */
1478           my_setopt(curl, CURLOPT_HTTP09_ALLOWED,
1479                     config->http09_allowed ? 1L : 0L);
1480           if(result) {
1481             errorf(global, "HTTP/0.9 is not supported in this build!\n");
1482             return result;
1483           }
1484 
1485         } /* (proto_http) */
1486 
1487         if(proto_ftp)
1488           my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport);
1489         my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
1490                   config->low_speed_limit);
1491         my_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time);
1492         my_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE,
1493                   config->sendpersecond);
1494         my_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE,
1495                   config->recvpersecond);
1496 
1497         if(config->use_resume)
1498           my_setopt(curl, CURLOPT_RESUME_FROM_LARGE, config->resume_from);
1499         else
1500           my_setopt(curl, CURLOPT_RESUME_FROM_LARGE, CURL_OFF_T_C(0));
1501 
1502         my_setopt_str(curl, CURLOPT_KEYPASSWD, config->key_passwd);
1503         my_setopt_str(curl, CURLOPT_PROXY_KEYPASSWD, config->proxy_key_passwd);
1504 
1505         if(use_proto == proto_scp || use_proto == proto_sftp) {
1506 
1507           /* SSH and SSL private key uses same command-line option */
1508           /* new in libcurl 7.16.1 */
1509           my_setopt_str(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key);
1510           /* new in libcurl 7.16.1 */
1511           my_setopt_str(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey);
1512 
1513           /* new in libcurl 7.17.1: SSH host key md5 checking allows us
1514              to fail if we are not talking to who we think we should */
1515           my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5,
1516                         config->hostpubmd5);
1517 
1518           /* new in libcurl 7.80.0: SSH host key sha256 checking allows us
1519              to fail if we are not talking to who we think we should */
1520           my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256,
1521               config->hostpubsha256);
1522 
1523           /* new in libcurl 7.56.0 */
1524           if(config->ssh_compression)
1525             my_setopt(curl, CURLOPT_SSH_COMPRESSION, 1L);
1526         }
1527 
1528         if(config->cacert)
1529           my_setopt_str(curl, CURLOPT_CAINFO, config->cacert);
1530         if(config->proxy_cacert)
1531           my_setopt_str(curl, CURLOPT_PROXY_CAINFO, config->proxy_cacert);
1532 
1533         if(config->capath) {
1534           result = res_setopt_str(curl, CURLOPT_CAPATH, config->capath);
1535           if(result == CURLE_NOT_BUILT_IN) {
1536             warnf(global, "ignoring %s, not supported by libcurl\n",
1537                   capath_from_env?
1538                   "SSL_CERT_DIR environment variable":"--capath");
1539           }
1540           else if(result)
1541             break;
1542         }
1543         /* For the time being if --proxy-capath is not set then we use the
1544            --capath value for it, if any. See #1257 */
1545         if(config->proxy_capath || config->capath) {
1546           result = res_setopt_str(curl, CURLOPT_PROXY_CAPATH,
1547                                   (config->proxy_capath ?
1548                                    config->proxy_capath :
1549                                    config->capath));
1550           if(result == CURLE_NOT_BUILT_IN) {
1551             if(config->proxy_capath) {
1552               warnf(global,
1553                     "ignoring --proxy-capath, not supported by libcurl\n");
1554             }
1555           }
1556           else if(result)
1557             break;
1558         }
1559 
1560         if(config->crlfile)
1561           my_setopt_str(curl, CURLOPT_CRLFILE, config->crlfile);
1562         if(config->proxy_crlfile)
1563           my_setopt_str(curl, CURLOPT_PROXY_CRLFILE, config->proxy_crlfile);
1564         else if(config->crlfile) /* CURLOPT_PROXY_CRLFILE default is crlfile */
1565           my_setopt_str(curl, CURLOPT_PROXY_CRLFILE, config->crlfile);
1566 
1567         if(config->pinnedpubkey)
1568           my_setopt_str(curl, CURLOPT_PINNEDPUBLICKEY, config->pinnedpubkey);
1569 
1570         if(config->ssl_ec_curves)
1571           my_setopt_str(curl, CURLOPT_SSL_EC_CURVES, config->ssl_ec_curves);
1572 
1573         if(config->writeout)
1574           my_setopt_str(curl, CURLOPT_CERTINFO, 1L);
1575 
1576         if(feature_ssl) {
1577           /* Check if config->cert is a PKCS#11 URI and set the
1578            * config->cert_type if necessary */
1579           if(config->cert) {
1580             if(!config->cert_type) {
1581               if(is_pkcs11_uri(config->cert)) {
1582                 config->cert_type = strdup("ENG");
1583               }
1584             }
1585           }
1586 
1587           /* Check if config->key is a PKCS#11 URI and set the
1588            * config->key_type if necessary */
1589           if(config->key) {
1590             if(!config->key_type) {
1591               if(is_pkcs11_uri(config->key)) {
1592                 config->key_type = strdup("ENG");
1593               }
1594             }
1595           }
1596 
1597           /* Check if config->proxy_cert is a PKCS#11 URI and set the
1598            * config->proxy_type if necessary */
1599           if(config->proxy_cert) {
1600             if(!config->proxy_cert_type) {
1601               if(is_pkcs11_uri(config->proxy_cert)) {
1602                 config->proxy_cert_type = strdup("ENG");
1603               }
1604             }
1605           }
1606 
1607           /* Check if config->proxy_key is a PKCS#11 URI and set the
1608            * config->proxy_key_type if necessary */
1609           if(config->proxy_key) {
1610             if(!config->proxy_key_type) {
1611               if(is_pkcs11_uri(config->proxy_key)) {
1612                 config->proxy_key_type = strdup("ENG");
1613               }
1614             }
1615           }
1616 
1617           /* In debug build of curl tool, using
1618            *    --cert loadmem=<filename>:<password> --cert-type p12
1619            *  must do the same thing as classic:
1620            *    --cert <filename>:<password> --cert-type p12
1621            *  but is designed to test blob */
1622 #if defined(CURLDEBUG) || defined(DEBUGBUILD)
1623           if(config->cert && (strlen(config->cert) > 8) &&
1624              (memcmp(config->cert, "loadmem=",8) == 0)) {
1625             FILE *fInCert = fopen(config->cert + 8, "rb");
1626             void *certdata = NULL;
1627             long filesize = 0;
1628             bool continue_reading = fInCert != NULL;
1629             if(continue_reading)
1630               continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
1631             if(continue_reading)
1632               filesize = ftell(fInCert);
1633             if(filesize < 0)
1634               continue_reading = FALSE;
1635             if(continue_reading)
1636               continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
1637             if(continue_reading)
1638               certdata = malloc(((size_t)filesize) + 1);
1639             if((!certdata) ||
1640                 ((int)fread(certdata, (size_t)filesize, 1, fInCert) != 1))
1641               continue_reading = FALSE;
1642             if(fInCert)
1643               fclose(fInCert);
1644             if((filesize > 0) && continue_reading) {
1645               struct curl_blob structblob;
1646               structblob.data = certdata;
1647               structblob.len = (size_t)filesize;
1648               structblob.flags = CURL_BLOB_COPY;
1649               my_setopt_str(curl, CURLOPT_SSLCERT_BLOB, &structblob);
1650               /* if test run well, we are sure we don't reuse
1651                * original mem pointer */
1652               memset(certdata, 0, (size_t)filesize);
1653             }
1654             free(certdata);
1655           }
1656           else
1657 #endif
1658           my_setopt_str(curl, CURLOPT_SSLCERT, config->cert);
1659           my_setopt_str(curl, CURLOPT_PROXY_SSLCERT, config->proxy_cert);
1660           my_setopt_str(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
1661           my_setopt_str(curl, CURLOPT_PROXY_SSLCERTTYPE,
1662                         config->proxy_cert_type);
1663 
1664 
1665 #if defined(CURLDEBUG) || defined(DEBUGBUILD)
1666           if(config->key && (strlen(config->key) > 8) &&
1667              (memcmp(config->key, "loadmem=",8) == 0)) {
1668             FILE *fInCert = fopen(config->key + 8, "rb");
1669             void *certdata = NULL;
1670             long filesize = 0;
1671             bool continue_reading = fInCert != NULL;
1672             if(continue_reading)
1673               continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
1674             if(continue_reading)
1675               filesize = ftell(fInCert);
1676             if(filesize < 0)
1677               continue_reading = FALSE;
1678             if(continue_reading)
1679               continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
1680             if(continue_reading)
1681               certdata = malloc(((size_t)filesize) + 1);
1682             if((!certdata) ||
1683                 ((int)fread(certdata, (size_t)filesize, 1, fInCert) != 1))
1684               continue_reading = FALSE;
1685             if(fInCert)
1686               fclose(fInCert);
1687             if((filesize > 0) && continue_reading) {
1688               struct curl_blob structblob;
1689               structblob.data = certdata;
1690               structblob.len = (size_t)filesize;
1691               structblob.flags = CURL_BLOB_COPY;
1692               my_setopt_str(curl, CURLOPT_SSLKEY_BLOB, &structblob);
1693               /* if test run well, we are sure we don't reuse
1694                * original mem pointer */
1695               memset(certdata, 0, (size_t)filesize);
1696             }
1697             free(certdata);
1698           }
1699           else
1700 #endif
1701           my_setopt_str(curl, CURLOPT_SSLKEY, config->key);
1702           my_setopt_str(curl, CURLOPT_PROXY_SSLKEY, config->proxy_key);
1703           my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type);
1704           my_setopt_str(curl, CURLOPT_PROXY_SSLKEYTYPE,
1705                         config->proxy_key_type);
1706           my_setopt_str(curl, CURLOPT_AWS_SIGV4,
1707                         config->aws_sigv4);
1708 
1709           if(config->insecure_ok) {
1710             my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
1711             my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
1712           }
1713           else {
1714             my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
1715             /* libcurl default is strict verifyhost -> 2L   */
1716             /* my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); */
1717           }
1718 
1719           if(config->doh_insecure_ok) {
1720             my_setopt(curl, CURLOPT_DOH_SSL_VERIFYPEER, 0L);
1721             my_setopt(curl, CURLOPT_DOH_SSL_VERIFYHOST, 0L);
1722           }
1723 
1724           if(config->proxy_insecure_ok) {
1725             my_setopt(curl, CURLOPT_PROXY_SSL_VERIFYPEER, 0L);
1726             my_setopt(curl, CURLOPT_PROXY_SSL_VERIFYHOST, 0L);
1727           }
1728           else {
1729             my_setopt(curl, CURLOPT_PROXY_SSL_VERIFYPEER, 1L);
1730           }
1731 
1732           if(config->verifystatus)
1733             my_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
1734 
1735           if(config->doh_verifystatus)
1736             my_setopt(curl, CURLOPT_DOH_SSL_VERIFYSTATUS, 1L);
1737 
1738           if(config->falsestart)
1739             my_setopt(curl, CURLOPT_SSL_FALSESTART, 1L);
1740 
1741           my_setopt_enum(curl, CURLOPT_SSLVERSION,
1742                          config->ssl_version | config->ssl_version_max);
1743           if(config->proxy)
1744             my_setopt_enum(curl, CURLOPT_PROXY_SSLVERSION,
1745                            config->proxy_ssl_version);
1746 
1747           {
1748             long mask =
1749               (config->ssl_allow_beast ?
1750                CURLSSLOPT_ALLOW_BEAST : 0) |
1751               (config->ssl_no_revoke ?
1752                CURLSSLOPT_NO_REVOKE : 0) |
1753               (config->ssl_revoke_best_effort ?
1754                CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
1755               (config->native_ca_store ?
1756                CURLSSLOPT_NATIVE_CA : 0) |
1757               (config->ssl_auto_client_cert ?
1758                CURLSSLOPT_AUTO_CLIENT_CERT : 0);
1759 
1760             if(mask)
1761               my_setopt_bitmask(curl, CURLOPT_SSL_OPTIONS, mask);
1762           }
1763 
1764           {
1765             long mask =
1766               (config->proxy_ssl_allow_beast ?
1767                CURLSSLOPT_ALLOW_BEAST : 0) |
1768               (config->proxy_ssl_auto_client_cert ?
1769                CURLSSLOPT_AUTO_CLIENT_CERT : 0);
1770 
1771             if(mask)
1772               my_setopt_bitmask(curl, CURLOPT_PROXY_SSL_OPTIONS, mask);
1773           }
1774         }
1775 
1776         if(config->path_as_is)
1777           my_setopt(curl, CURLOPT_PATH_AS_IS, 1L);
1778 
1779         if((use_proto == proto_scp || use_proto == proto_sftp) &&
1780            !config->insecure_ok) {
1781           char *known = findfile(".ssh/known_hosts", FALSE);
1782           if(known) {
1783             /* new in curl 7.19.6 */
1784             result = res_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, known);
1785             curl_free(known);
1786             if(result == CURLE_UNKNOWN_OPTION)
1787               /* libssh2 version older than 1.1.1 */
1788               result = CURLE_OK;
1789             if(result)
1790               break;
1791           }
1792           else
1793             warnf(global, "Couldn't find a known_hosts file!");
1794         }
1795 
1796         if(config->no_body || config->remote_time) {
1797           /* no body or use remote time */
1798           my_setopt(curl, CURLOPT_FILETIME, 1L);
1799         }
1800 
1801         my_setopt(curl, CURLOPT_CRLF, config->crlf?1L:0L);
1802         my_setopt_slist(curl, CURLOPT_QUOTE, config->quote);
1803         my_setopt_slist(curl, CURLOPT_POSTQUOTE, config->postquote);
1804         my_setopt_slist(curl, CURLOPT_PREQUOTE, config->prequote);
1805 
1806         if(config->cookies) {
1807           struct curlx_dynbuf cookies;
1808           struct curl_slist *cl;
1809 
1810           /* The maximum size needs to match MAX_NAME in cookie.h */
1811 #define MAX_COOKIE_LINE 4096
1812           curlx_dyn_init(&cookies, MAX_COOKIE_LINE);
1813           for(cl = config->cookies; cl; cl = cl->next) {
1814             if(cl == config->cookies)
1815               result = curlx_dyn_addf(&cookies, "%s", cl->data);
1816             else
1817               result = curlx_dyn_addf(&cookies, ";%s", cl->data);
1818 
1819             if(result) {
1820               warnf(global,
1821                     "skipped provided cookie, the cookie header "
1822                     "would go over %u bytes\n", MAX_COOKIE_LINE);
1823               break;
1824             }
1825           }
1826 
1827           my_setopt_str(curl, CURLOPT_COOKIE, curlx_dyn_ptr(&cookies));
1828           curlx_dyn_free(&cookies);
1829         }
1830 
1831         if(config->cookiefiles) {
1832           struct curl_slist *cfl;
1833 
1834           for(cfl = config->cookiefiles; cfl; cfl = cfl->next)
1835             my_setopt_str(curl, CURLOPT_COOKIEFILE, cfl->data);
1836         }
1837 
1838         /* new in libcurl 7.9 */
1839         if(config->cookiejar)
1840           my_setopt_str(curl, CURLOPT_COOKIEJAR, config->cookiejar);
1841 
1842         /* new in libcurl 7.9.7 */
1843         my_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession?1L:0L);
1844 
1845         my_setopt_enum(curl, CURLOPT_TIMECONDITION, (long)config->timecond);
1846         my_setopt(curl, CURLOPT_TIMEVALUE_LARGE, config->condtime);
1847         my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
1848         customrequest_helper(config, config->httpreq, config->customrequest);
1849         my_setopt(curl, CURLOPT_STDERR, stderr);
1850 
1851         /* three new ones in libcurl 7.3: */
1852         my_setopt_str(curl, CURLOPT_INTERFACE, config->iface);
1853         my_setopt_str(curl, CURLOPT_KRBLEVEL, config->krblevel);
1854         progressbarinit(&per->progressbar, config);
1855 
1856         if((global->progressmode == CURL_PROGRESS_BAR) &&
1857            !global->noprogress && !global->silent) {
1858           /* we want the alternative style, then we have to implement it
1859              ourselves! */
1860           my_setopt(curl, CURLOPT_XFERINFOFUNCTION, tool_progress_cb);
1861           my_setopt(curl, CURLOPT_XFERINFODATA, per);
1862         }
1863         else if(per->uploadfile && !strcmp(per->uploadfile, ".")) {
1864           /* when reading from stdin in non-blocking mode, we use the progress
1865              function to unpause a busy read */
1866           my_setopt(curl, CURLOPT_NOPROGRESS, 0L);
1867           my_setopt(curl, CURLOPT_XFERINFOFUNCTION, tool_readbusy_cb);
1868           my_setopt(curl, CURLOPT_XFERINFODATA, per);
1869         }
1870 
1871         /* new in libcurl 7.24.0: */
1872         if(config->dns_servers)
1873           my_setopt_str(curl, CURLOPT_DNS_SERVERS, config->dns_servers);
1874 
1875         /* new in libcurl 7.33.0: */
1876         if(config->dns_interface)
1877           my_setopt_str(curl, CURLOPT_DNS_INTERFACE, config->dns_interface);
1878         if(config->dns_ipv4_addr)
1879           my_setopt_str(curl, CURLOPT_DNS_LOCAL_IP4, config->dns_ipv4_addr);
1880         if(config->dns_ipv6_addr)
1881         my_setopt_str(curl, CURLOPT_DNS_LOCAL_IP6, config->dns_ipv6_addr);
1882 
1883         /* new in libcurl 7.6.2: */
1884         my_setopt_slist(curl, CURLOPT_TELNETOPTIONS, config->telnet_options);
1885 
1886         /* new in libcurl 7.7: */
1887         my_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, config->connecttimeout_ms);
1888 
1889         if(config->doh_url)
1890           my_setopt_str(curl, CURLOPT_DOH_URL, config->doh_url);
1891 
1892         if(config->cipher_list)
1893           my_setopt_str(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list);
1894 
1895         if(config->proxy_cipher_list)
1896           my_setopt_str(curl, CURLOPT_PROXY_SSL_CIPHER_LIST,
1897                         config->proxy_cipher_list);
1898 
1899         if(config->cipher13_list)
1900           my_setopt_str(curl, CURLOPT_TLS13_CIPHERS, config->cipher13_list);
1901 
1902         if(config->proxy_cipher13_list)
1903           my_setopt_str(curl, CURLOPT_PROXY_TLS13_CIPHERS,
1904                         config->proxy_cipher13_list);
1905 
1906         /* new in libcurl 7.9.2: */
1907         if(config->disable_epsv)
1908           /* disable it */
1909           my_setopt(curl, CURLOPT_FTP_USE_EPSV, 0L);
1910 
1911         /* new in libcurl 7.10.5 */
1912         if(config->disable_eprt)
1913           /* disable it */
1914           my_setopt(curl, CURLOPT_FTP_USE_EPRT, 0L);
1915 
1916         if(global->tracetype != TRACE_NONE) {
1917           my_setopt(curl, CURLOPT_DEBUGFUNCTION, tool_debug_cb);
1918           my_setopt(curl, CURLOPT_DEBUGDATA, config);
1919           my_setopt(curl, CURLOPT_VERBOSE, 1L);
1920         }
1921 
1922         /* new in curl 7.9.3 */
1923         if(config->engine) {
1924           result = res_setopt_str(curl, CURLOPT_SSLENGINE, config->engine);
1925           if(result)
1926             break;
1927         }
1928 
1929         /* new in curl 7.10.7, extended in 7.19.4. Modified to use
1930            CREATE_DIR_RETRY in 7.49.0 */
1931         my_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS,
1932                   (long)(config->ftp_create_dirs?
1933                          CURLFTP_CREATE_DIR_RETRY:
1934                          CURLFTP_CREATE_DIR_NONE));
1935 
1936         /* new in curl 7.10.8 */
1937         if(config->max_filesize)
1938           my_setopt(curl, CURLOPT_MAXFILESIZE_LARGE,
1939                     config->max_filesize);
1940 
1941         my_setopt(curl, CURLOPT_IPRESOLVE, config->ip_version);
1942 
1943         /* new in curl 7.15.5 */
1944         if(config->ftp_ssl_reqd)
1945           my_setopt_enum(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
1946 
1947         /* new in curl 7.11.0 */
1948         else if(config->ftp_ssl)
1949           my_setopt_enum(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_TRY);
1950 
1951         /* new in curl 7.16.0 */
1952         else if(config->ftp_ssl_control)
1953           my_setopt_enum(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_CONTROL);
1954 
1955         /* new in curl 7.16.1 */
1956         if(config->ftp_ssl_ccc)
1957           my_setopt_enum(curl, CURLOPT_FTP_SSL_CCC,
1958                          (long)config->ftp_ssl_ccc_mode);
1959 
1960         /* new in curl 7.19.4 */
1961         if(config->socks5_gssapi_nec)
1962           my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_NEC, 1L);
1963 
1964         /* new in curl 7.55.0 */
1965         if(config->socks5_auth)
1966           my_setopt_bitmask(curl, CURLOPT_SOCKS5_AUTH,
1967                             (long)config->socks5_auth);
1968 
1969         /* new in curl 7.43.0 */
1970         if(config->proxy_service_name)
1971           my_setopt_str(curl, CURLOPT_PROXY_SERVICE_NAME,
1972                         config->proxy_service_name);
1973 
1974         /* new in curl 7.43.0 */
1975         if(config->service_name)
1976           my_setopt_str(curl, CURLOPT_SERVICE_NAME,
1977                         config->service_name);
1978 
1979         /* curl 7.13.0 */
1980         my_setopt_str(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account);
1981         my_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl?1L:0L);
1982 
1983         /* curl 7.14.2 */
1984         my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip?1L:0L);
1985 
1986         /* curl 7.15.1 */
1987         if(proto_ftp)
1988           my_setopt(curl, CURLOPT_FTP_FILEMETHOD,
1989                     (long)config->ftp_filemethod);
1990 
1991         /* curl 7.15.2 */
1992         if(config->localport) {
1993           my_setopt(curl, CURLOPT_LOCALPORT, config->localport);
1994           my_setopt_str(curl, CURLOPT_LOCALPORTRANGE, config->localportrange);
1995         }
1996 
1997         /* curl 7.15.5 */
1998         my_setopt_str(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER,
1999                       config->ftp_alternative_to_user);
2000 
2001         /* curl 7.16.0 */
2002         if(config->disable_sessionid)
2003           /* disable it */
2004           my_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, 0L);
2005 
2006         /* curl 7.16.2 */
2007         if(config->raw) {
2008           my_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, 0L);
2009           my_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, 0L);
2010         }
2011 
2012         /* curl 7.17.1 */
2013         if(!config->nokeepalive) {
2014           my_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
2015           if(config->alivetime) {
2016             my_setopt(curl, CURLOPT_TCP_KEEPIDLE, config->alivetime);
2017             my_setopt(curl, CURLOPT_TCP_KEEPINTVL, config->alivetime);
2018           }
2019         }
2020         else
2021           my_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L);
2022 
2023         /* curl 7.20.0 */
2024         if(config->tftp_blksize && proto_tftp)
2025           my_setopt(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize);
2026 
2027         if(config->mail_from)
2028           my_setopt_str(curl, CURLOPT_MAIL_FROM, config->mail_from);
2029 
2030         if(config->mail_rcpt)
2031           my_setopt_slist(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt);
2032 
2033         /* curl 7.69.x */
2034         my_setopt(curl, CURLOPT_MAIL_RCPT_ALLLOWFAILS,
2035           config->mail_rcpt_allowfails ? 1L : 0L);
2036 
2037         /* curl 7.20.x */
2038         if(config->ftp_pret)
2039           my_setopt(curl, CURLOPT_FTP_USE_PRET, 1L);
2040 
2041         if(config->create_file_mode)
2042           my_setopt(curl, CURLOPT_NEW_FILE_PERMS, config->create_file_mode);
2043 
2044         if(config->proto_present)
2045           my_setopt_str(curl, CURLOPT_PROTOCOLS_STR, config->proto_str);
2046         if(config->proto_redir_present)
2047           my_setopt_str(curl, CURLOPT_REDIR_PROTOCOLS_STR,
2048                         config->proto_redir_str);
2049 
2050         if(config->content_disposition
2051            && (urlnode->flags & GETOUT_USEREMOTE))
2052           hdrcbdata->honor_cd_filename = TRUE;
2053         else
2054           hdrcbdata->honor_cd_filename = FALSE;
2055 
2056         hdrcbdata->outs = outs;
2057         hdrcbdata->heads = heads;
2058         hdrcbdata->etag_save = etag_save;
2059         hdrcbdata->global = global;
2060         hdrcbdata->config = config;
2061 
2062         my_setopt(curl, CURLOPT_HEADERFUNCTION, tool_header_cb);
2063         my_setopt(curl, CURLOPT_HEADERDATA, per);
2064 
2065         if(config->resolve)
2066           /* new in 7.21.3 */
2067           my_setopt_slist(curl, CURLOPT_RESOLVE, config->resolve);
2068 
2069         if(config->connect_to)
2070           /* new in 7.49.0 */
2071           my_setopt_slist(curl, CURLOPT_CONNECT_TO, config->connect_to);
2072 
2073         /* new in 7.21.4 */
2074         if(feature_tls_srp) {
2075           if(config->tls_username)
2076             my_setopt_str(curl, CURLOPT_TLSAUTH_USERNAME,
2077                           config->tls_username);
2078           if(config->tls_password)
2079             my_setopt_str(curl, CURLOPT_TLSAUTH_PASSWORD,
2080                           config->tls_password);
2081           if(config->tls_authtype)
2082             my_setopt_str(curl, CURLOPT_TLSAUTH_TYPE,
2083                           config->tls_authtype);
2084           if(config->proxy_tls_username)
2085             my_setopt_str(curl, CURLOPT_PROXY_TLSAUTH_USERNAME,
2086                           config->proxy_tls_username);
2087           if(config->proxy_tls_password)
2088             my_setopt_str(curl, CURLOPT_PROXY_TLSAUTH_PASSWORD,
2089                           config->proxy_tls_password);
2090           if(config->proxy_tls_authtype)
2091             my_setopt_str(curl, CURLOPT_PROXY_TLSAUTH_TYPE,
2092                           config->proxy_tls_authtype);
2093         }
2094 
2095         /* new in 7.22.0 */
2096         if(config->gssapi_delegation)
2097           my_setopt_str(curl, CURLOPT_GSSAPI_DELEGATION,
2098                         config->gssapi_delegation);
2099 
2100         if(config->mail_auth)
2101           my_setopt_str(curl, CURLOPT_MAIL_AUTH, config->mail_auth);
2102 
2103         /* new in 7.66.0 */
2104         if(config->sasl_authzid)
2105           my_setopt_str(curl, CURLOPT_SASL_AUTHZID, config->sasl_authzid);
2106 
2107         /* new in 7.31.0 */
2108         if(config->sasl_ir)
2109           my_setopt(curl, CURLOPT_SASL_IR, 1L);
2110 
2111         if(config->noalpn) {
2112           my_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, 0L);
2113         }
2114 
2115         /* new in 7.40.0, abstract support added in 7.53.0 */
2116         if(config->unix_socket_path) {
2117           if(config->abstract_unix_socket) {
2118             my_setopt_str(curl, CURLOPT_ABSTRACT_UNIX_SOCKET,
2119                           config->unix_socket_path);
2120           }
2121           else {
2122             my_setopt_str(curl, CURLOPT_UNIX_SOCKET_PATH,
2123                           config->unix_socket_path);
2124           }
2125         }
2126 
2127         /* new in 7.45.0 */
2128         if(config->proto_default)
2129           my_setopt_str(curl, CURLOPT_DEFAULT_PROTOCOL, config->proto_default);
2130 
2131         /* new in 7.47.0 */
2132         if(config->expect100timeout_ms > 0)
2133           my_setopt_str(curl, CURLOPT_EXPECT_100_TIMEOUT_MS,
2134                         config->expect100timeout_ms);
2135 
2136         /* new in 7.48.0 */
2137         if(config->tftp_no_options && proto_tftp)
2138           my_setopt(curl, CURLOPT_TFTP_NO_OPTIONS, 1L);
2139 
2140         /* new in 7.59.0 */
2141         if(config->happy_eyeballs_timeout_ms != CURL_HET_DEFAULT)
2142           my_setopt(curl, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS,
2143                     config->happy_eyeballs_timeout_ms);
2144 
2145         /* new in 7.60.0 */
2146         if(config->haproxy_protocol)
2147           my_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L);
2148 
2149         if(config->disallow_username_in_url)
2150           my_setopt(curl, CURLOPT_DISALLOW_USERNAME_IN_URL, 1L);
2151 
2152         if(config->altsvc)
2153           my_setopt_str(curl, CURLOPT_ALTSVC, config->altsvc);
2154 
2155         if(config->hsts)
2156           my_setopt_str(curl, CURLOPT_HSTS, config->hsts);
2157 
2158         /* initialize retry vars for loop below */
2159         per->retry_sleep_default = (config->retry_delay) ?
2160           config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */
2161         per->retry_numretries = config->req_retry;
2162         per->retry_sleep = per->retry_sleep_default; /* ms */
2163         per->retrystart = tvnow();
2164 
2165         state->li++;
2166         /* Here's looping around each globbed URL */
2167         if(state->li >= urlnum) {
2168           state->li = 0;
2169           state->urlnum = 0; /* forced reglob of URLs */
2170           glob_cleanup(state->urls);
2171           state->urls = NULL;
2172           state->up++;
2173           Curl_safefree(state->uploadfile); /* clear it to get the next */
2174         }
2175       }
2176       else {
2177         /* Free this URL node data without destroying the
2178            node itself nor modifying next pointer. */
2179         Curl_safefree(urlnode->outfile);
2180         Curl_safefree(urlnode->infile);
2181         urlnode->flags = 0;
2182         glob_cleanup(state->urls);
2183         state->urls = NULL;
2184         state->urlnum = 0;
2185 
2186         Curl_safefree(state->outfiles);
2187         Curl_safefree(state->uploadfile);
2188         if(state->inglob) {
2189           /* Free list of globbed upload files */
2190           glob_cleanup(state->inglob);
2191           state->inglob = NULL;
2192         }
2193         config->state.urlnode = urlnode->next;
2194         state->up = 0;
2195         continue;
2196       }
2197     }
2198     break;
2199   }
2200   Curl_safefree(state->outfiles);
2201 
2202   if(!*added || result) {
2203     *added = FALSE;
2204     single_transfer_cleanup(config);
2205   }
2206   return result;
2207 }
2208 
2209 static long all_added; /* number of easy handles currently added */
2210 
2211 /*
2212  * add_parallel_transfers() sets 'morep' to TRUE if there are more transfers
2213  * to add even after this call returns. sets 'addedp' to TRUE if one or more
2214  * transfers were added.
2215  */
add_parallel_transfers(struct GlobalConfig * global,CURLM * multi,CURLSH * share,bool * morep,bool * addedp)2216 static CURLcode add_parallel_transfers(struct GlobalConfig *global,
2217                                        CURLM *multi,
2218                                        CURLSH *share,
2219                                        bool *morep,
2220                                        bool *addedp)
2221 {
2222   struct per_transfer *per;
2223   CURLcode result = CURLE_OK;
2224   CURLMcode mcode;
2225   bool sleeping = FALSE;
2226   char *errorbuf;
2227   *addedp = FALSE;
2228   *morep = FALSE;
2229   if(all_pers < (global->parallel_max*2)) {
2230     result = create_transfer(global, share, addedp);
2231     if(result)
2232       return result;
2233   }
2234   for(per = transfers; per && (all_added < global->parallel_max);
2235       per = per->next) {
2236     bool getadded = FALSE;
2237     if(per->added)
2238       /* already added */
2239       continue;
2240     if(per->startat && (time(NULL) < per->startat)) {
2241       /* this is still delaying */
2242       sleeping = TRUE;
2243       continue;
2244     }
2245     per->added = TRUE;
2246 
2247     result = pre_transfer(global, per);
2248     if(result)
2249       return result;
2250 
2251     errorbuf = malloc(CURL_ERROR_SIZE);
2252     if(!errorbuf)
2253       return CURLE_OUT_OF_MEMORY;
2254 
2255     /* parallel connect means that we don't set PIPEWAIT since pipewait
2256        will make libcurl prefer multiplexing */
2257     (void)curl_easy_setopt(per->curl, CURLOPT_PIPEWAIT,
2258                            global->parallel_connect ? 0L : 1L);
2259     (void)curl_easy_setopt(per->curl, CURLOPT_PRIVATE, per);
2260     (void)curl_easy_setopt(per->curl, CURLOPT_XFERINFOFUNCTION, xferinfo_cb);
2261     (void)curl_easy_setopt(per->curl, CURLOPT_XFERINFODATA, per);
2262     (void)curl_easy_setopt(per->curl, CURLOPT_NOPROGRESS, 0L);
2263 
2264     mcode = curl_multi_add_handle(multi, per->curl);
2265     if(mcode) {
2266       DEBUGASSERT(mcode == CURLM_OUT_OF_MEMORY);
2267       result = CURLE_OUT_OF_MEMORY;
2268     }
2269 
2270     if(!result)
2271       result = create_transfer(global, share, &getadded);
2272     if(result) {
2273       free(errorbuf);
2274       return result;
2275     }
2276     errorbuf[0] = 0;
2277     (void)curl_easy_setopt(per->curl, CURLOPT_ERRORBUFFER, errorbuf);
2278     per->errorbuffer = errorbuf;
2279     per->added = TRUE;
2280     all_added++;
2281     *addedp = TRUE;
2282   }
2283   *morep = (per || sleeping) ? TRUE : FALSE;
2284   return CURLE_OK;
2285 }
2286 
parallel_transfers(struct GlobalConfig * global,CURLSH * share)2287 static CURLcode parallel_transfers(struct GlobalConfig *global,
2288                                    CURLSH *share)
2289 {
2290   CURLM *multi;
2291   CURLMcode mcode = CURLM_OK;
2292   CURLcode result = CURLE_OK;
2293   int still_running = 1;
2294   struct timeval start = tvnow();
2295   bool more_transfers;
2296   bool added_transfers;
2297   /* wrapitup is set TRUE after a critical error occurs to end all transfers */
2298   bool wrapitup = FALSE;
2299   /* wrapitup_processed is set TRUE after the per transfer abort flag is set */
2300   bool wrapitup_processed = FALSE;
2301   time_t tick = time(NULL);
2302 
2303   multi = curl_multi_init();
2304   if(!multi)
2305     return CURLE_OUT_OF_MEMORY;
2306 
2307   result = add_parallel_transfers(global, multi, share,
2308                                   &more_transfers, &added_transfers);
2309   if(result) {
2310     curl_multi_cleanup(multi);
2311     return result;
2312   }
2313 
2314   while(!mcode && (still_running || more_transfers)) {
2315     /* If stopping prematurely (eg due to a --fail-early condition) then signal
2316        that any transfers in the multi should abort (via progress callback). */
2317     if(wrapitup) {
2318       if(!still_running)
2319         break;
2320       if(!wrapitup_processed) {
2321         struct per_transfer *per;
2322         for(per = transfers; per; per = per->next) {
2323           if(per->added)
2324             per->abort = TRUE;
2325         }
2326         wrapitup_processed = TRUE;
2327       }
2328     }
2329 
2330     mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL);
2331     if(!mcode)
2332       mcode = curl_multi_perform(multi, &still_running);
2333 
2334     progress_meter(global, &start, FALSE);
2335 
2336     if(!mcode) {
2337       int rc;
2338       CURLMsg *msg;
2339       bool checkmore = FALSE;
2340       do {
2341         msg = curl_multi_info_read(multi, &rc);
2342         if(msg) {
2343           bool retry;
2344           long delay;
2345           struct per_transfer *ended;
2346           CURL *easy = msg->easy_handle;
2347           CURLcode tres = msg->data.result;
2348           curl_easy_getinfo(easy, CURLINFO_PRIVATE, (void *)&ended);
2349           curl_multi_remove_handle(multi, easy);
2350 
2351           if(ended->abort && (tres == CURLE_ABORTED_BY_CALLBACK) &&
2352              ended->errorbuffer) {
2353             msnprintf(ended->errorbuffer, CURL_ERROR_SIZE,
2354                       "Transfer aborted due to critical error "
2355                       "in another transfer");
2356           }
2357           tres = post_per_transfer(global, ended, tres, &retry, &delay);
2358           progress_finalize(ended); /* before it goes away */
2359           all_added--; /* one fewer added */
2360           checkmore = TRUE;
2361           if(retry) {
2362             ended->added = FALSE; /* add it again */
2363             /* we delay retries in full integer seconds only */
2364             ended->startat = delay ? time(NULL) + delay/1000 : 0;
2365           }
2366           else {
2367             /* result receives this transfer's error unless the transfer was
2368                marked for abort due to a critical error in another transfer */
2369             if(tres && (!ended->abort || !result))
2370               result = tres;
2371             if(is_fatal_error(result) || (result && global->fail_early))
2372               wrapitup = TRUE;
2373             (void)del_per_transfer(ended);
2374           }
2375         }
2376       } while(msg);
2377       if(wrapitup) {
2378         if(still_running)
2379           continue;
2380         else
2381           break;
2382       }
2383       if(!checkmore) {
2384         time_t tock = time(NULL);
2385         if(tick != tock) {
2386           checkmore = TRUE;
2387           tick = tock;
2388         }
2389       }
2390       if(checkmore) {
2391         /* one or more transfers completed, add more! */
2392         CURLcode tres = add_parallel_transfers(global, multi, share,
2393                                                &more_transfers,
2394                                                &added_transfers);
2395         if(tres)
2396           result = tres;
2397         if(added_transfers)
2398           /* we added new ones, make sure the loop doesn't exit yet */
2399           still_running = 1;
2400       }
2401       if(is_fatal_error(result) || (result && global->fail_early))
2402         wrapitup = TRUE;
2403     }
2404   }
2405 
2406   (void)progress_meter(global, &start, TRUE);
2407 
2408   /* Make sure to return some kind of error if there was a multi problem */
2409   if(mcode) {
2410     result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY :
2411       /* The other multi errors should never happen, so return
2412          something suitably generic */
2413       CURLE_BAD_FUNCTION_ARGUMENT;
2414   }
2415 
2416   curl_multi_cleanup(multi);
2417 
2418   return result;
2419 }
2420 
serial_transfers(struct GlobalConfig * global,CURLSH * share)2421 static CURLcode serial_transfers(struct GlobalConfig *global,
2422                                  CURLSH *share)
2423 {
2424   CURLcode returncode = CURLE_OK;
2425   CURLcode result = CURLE_OK;
2426   struct per_transfer *per;
2427   bool added = FALSE;
2428 
2429   result = create_transfer(global, share, &added);
2430   if(result)
2431     return result;
2432   if(!added) {
2433     errorf(global, "no transfer performed\n");
2434     return CURLE_READ_ERROR;
2435   }
2436   for(per = transfers; per;) {
2437     bool retry;
2438     long delay_ms;
2439     bool bailout = FALSE;
2440     struct timeval start;
2441     result = pre_transfer(global, per);
2442     if(result)
2443       break;
2444 
2445     if(global->libcurl) {
2446       result = easysrc_perform();
2447       if(result)
2448         break;
2449     }
2450     start = tvnow();
2451 #ifdef CURLDEBUG
2452     if(global->test_event_based)
2453       result = curl_easy_perform_ev(per->curl);
2454     else
2455 #endif
2456       result = curl_easy_perform(per->curl);
2457 
2458     returncode = post_per_transfer(global, per, result, &retry, &delay_ms);
2459     if(retry) {
2460       tool_go_sleep(delay_ms);
2461       continue;
2462     }
2463 
2464     /* Bail out upon critical errors or --fail-early */
2465     if(is_fatal_error(returncode) || (returncode && global->fail_early))
2466       bailout = TRUE;
2467     else {
2468       /* setup the next one just before we delete this */
2469       result = create_transfer(global, share, &added);
2470       if(result) {
2471         returncode = result;
2472         bailout = TRUE;
2473       }
2474     }
2475 
2476     per = del_per_transfer(per);
2477 
2478     if(bailout)
2479       break;
2480 
2481     if(per && global->ms_per_transfer) {
2482       /* how long time did the most recent transfer take in number of
2483          milliseconds */
2484       long milli = tvdiff(tvnow(), start);
2485       if(milli < global->ms_per_transfer) {
2486         notef(global, "Transfer took %ld ms, waits %ldms as set by --rate\n",
2487               milli, global->ms_per_transfer - milli);
2488         /* The transfer took less time than wanted. Wait a little. */
2489         tool_go_sleep(global->ms_per_transfer - milli);
2490       }
2491     }
2492   }
2493   if(returncode)
2494     /* returncode errors have priority */
2495     result = returncode;
2496 
2497   if(result)
2498     single_transfer_cleanup(global->current);
2499 
2500   return result;
2501 }
2502 
2503 /* setup a transfer for the given config */
transfer_per_config(struct GlobalConfig * global,struct OperationConfig * config,CURLSH * share,bool * added)2504 static CURLcode transfer_per_config(struct GlobalConfig *global,
2505                                     struct OperationConfig *config,
2506                                     CURLSH *share,
2507                                     bool *added)
2508 {
2509   CURLcode result = CURLE_OK;
2510   bool capath_from_env;
2511   *added = FALSE;
2512 
2513   /* Check we have a url */
2514   if(!config->url_list || !config->url_list->url) {
2515     helpf(stderr, "(%d) no URL specified!\n", CURLE_FAILED_INIT);
2516     return CURLE_FAILED_INIT;
2517   }
2518 
2519   /* On WIN32 we can't set the path to curl-ca-bundle.crt
2520    * at compile time. So we look here for the file in two ways:
2521    * 1: look at the environment variable CURL_CA_BUNDLE for a path
2522    * 2: if #1 isn't found, use the windows API function SearchPath()
2523    *    to find it along the app's path (includes app's dir and CWD)
2524    *
2525    * We support the environment variable thing for non-Windows platforms
2526    * too. Just for the sake of it.
2527    */
2528   capath_from_env = false;
2529   if(!config->cacert &&
2530      !config->capath &&
2531      (!config->insecure_ok || (config->doh_url && !config->doh_insecure_ok))) {
2532     CURL *curltls = curl_easy_init();
2533     struct curl_tlssessioninfo *tls_backend_info = NULL;
2534 
2535     /* With the addition of CAINFO support for Schannel, this search could find
2536      * a certificate bundle that was previously ignored. To maintain backward
2537      * compatibility, only perform this search if not using Schannel.
2538      */
2539     result = curl_easy_getinfo(curltls, CURLINFO_TLS_SSL_PTR,
2540                                &tls_backend_info);
2541     if(result) {
2542       curl_easy_cleanup(curltls);
2543       return result;
2544     }
2545 
2546     /* Set the CA cert locations specified in the environment. For Windows if
2547      * no environment-specified filename is found then check for CA bundle
2548      * default filename curl-ca-bundle.crt in the user's PATH.
2549      *
2550      * If Schannel is the selected SSL backend then these locations are
2551      * ignored. We allow setting CA location for schannel only when explicitly
2552      * specified by the user via CURLOPT_CAINFO / --cacert.
2553      */
2554     if(tls_backend_info->backend != CURLSSLBACKEND_SCHANNEL) {
2555       char *env;
2556       env = curlx_getenv("CURL_CA_BUNDLE");
2557       if(env) {
2558         config->cacert = strdup(env);
2559         if(!config->cacert) {
2560           curl_free(env);
2561           curl_easy_cleanup(curltls);
2562           errorf(global, "out of memory\n");
2563           return CURLE_OUT_OF_MEMORY;
2564         }
2565       }
2566       else {
2567         env = curlx_getenv("SSL_CERT_DIR");
2568         if(env) {
2569           config->capath = strdup(env);
2570           if(!config->capath) {
2571             curl_free(env);
2572             curl_easy_cleanup(curltls);
2573             helpf(stderr, "out of memory\n");
2574             return CURLE_OUT_OF_MEMORY;
2575           }
2576           capath_from_env = true;
2577         }
2578         else {
2579           env = curlx_getenv("SSL_CERT_FILE");
2580           if(env) {
2581             config->cacert = strdup(env);
2582             if(!config->cacert) {
2583               curl_free(env);
2584               curl_easy_cleanup(curltls);
2585               errorf(global, "out of memory\n");
2586               return CURLE_OUT_OF_MEMORY;
2587             }
2588           }
2589         }
2590       }
2591 
2592       if(env)
2593         curl_free(env);
2594 #ifdef WIN32
2595       else {
2596         result = FindWin32CACert(config, tls_backend_info->backend,
2597                                  TEXT("curl-ca-bundle.crt"));
2598       }
2599 #endif
2600     }
2601     curl_easy_cleanup(curltls);
2602   }
2603 
2604   if(!result)
2605     result = single_transfer(global, config, share, capath_from_env, added);
2606 
2607   return result;
2608 }
2609 
2610 /*
2611  * 'create_transfer' gets the details and sets up a new transfer if 'added'
2612  * returns TRUE.
2613  */
create_transfer(struct GlobalConfig * global,CURLSH * share,bool * added)2614 static CURLcode create_transfer(struct GlobalConfig *global,
2615                                 CURLSH *share,
2616                                 bool *added)
2617 {
2618   CURLcode result = CURLE_OK;
2619   *added = FALSE;
2620   while(global->current) {
2621     result = transfer_per_config(global, global->current, share, added);
2622     if(!result && !*added) {
2623       /* when one set is drained, continue to next */
2624       global->current = global->current->next;
2625       continue;
2626     }
2627     break;
2628   }
2629   return result;
2630 }
2631 
run_all_transfers(struct GlobalConfig * global,CURLSH * share,CURLcode result)2632 static CURLcode run_all_transfers(struct GlobalConfig *global,
2633                                   CURLSH *share,
2634                                   CURLcode result)
2635 {
2636   /* Save the values of noprogress and isatty to restore them later on */
2637   bool orig_noprogress = global->noprogress;
2638   bool orig_isatty = global->isatty;
2639   struct per_transfer *per;
2640 
2641   /* Time to actually do the transfers */
2642   if(!result) {
2643     if(global->parallel)
2644       result = parallel_transfers(global, share);
2645     else
2646       result = serial_transfers(global, share);
2647   }
2648 
2649   /* cleanup if there are any left */
2650   for(per = transfers; per;) {
2651     bool retry;
2652     long delay;
2653     CURLcode result2 = post_per_transfer(global, per, result, &retry, &delay);
2654     if(!result)
2655       /* don't overwrite the original error */
2656       result = result2;
2657 
2658     /* Free list of given URLs */
2659     clean_getout(per->config);
2660 
2661     per = del_per_transfer(per);
2662   }
2663 
2664   /* Reset the global config variables */
2665   global->noprogress = orig_noprogress;
2666   global->isatty = orig_isatty;
2667 
2668 
2669   return result;
2670 }
2671 
operate(struct GlobalConfig * global,int argc,argv_item_t argv[])2672 CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[])
2673 {
2674   CURLcode result = CURLE_OK;
2675   char *first_arg = argc > 1 ? curlx_convert_tchar_to_UTF8(argv[1]) : NULL;
2676 
2677 #ifdef HAVE_SETLOCALE
2678   /* Override locale for number parsing (only) */
2679   setlocale(LC_ALL, "");
2680   setlocale(LC_NUMERIC, "C");
2681 #endif
2682 
2683   /* Parse .curlrc if necessary */
2684   if((argc == 1) ||
2685      (first_arg && strncmp(first_arg, "-q", 2) &&
2686       !curl_strequal(first_arg, "--disable"))) {
2687     parseconfig(NULL, global); /* ignore possible failure */
2688 
2689     /* If we had no arguments then make sure a url was specified in .curlrc */
2690     if((argc < 2) && (!global->first->url_list)) {
2691       helpf(stderr, NULL);
2692       result = CURLE_FAILED_INIT;
2693     }
2694   }
2695 
2696   curlx_unicodefree(first_arg);
2697 
2698   if(!result) {
2699     /* Parse the command line arguments */
2700     ParameterError res = parse_args(global, argc, argv);
2701     if(res) {
2702       result = CURLE_OK;
2703 
2704       /* Check if we were asked for the help */
2705       if(res == PARAM_HELP_REQUESTED)
2706         tool_help(global->help_category);
2707       /* Check if we were asked for the manual */
2708       else if(res == PARAM_MANUAL_REQUESTED)
2709         hugehelp();
2710       /* Check if we were asked for the version information */
2711       else if(res == PARAM_VERSION_INFO_REQUESTED)
2712         tool_version_info();
2713       /* Check if we were asked to list the SSL engines */
2714       else if(res == PARAM_ENGINES_REQUESTED)
2715         tool_list_engines();
2716       else if(res == PARAM_LIBCURL_UNSUPPORTED_PROTOCOL)
2717         result = CURLE_UNSUPPORTED_PROTOCOL;
2718       else if(res == PARAM_READ_ERROR)
2719         result = CURLE_READ_ERROR;
2720       else
2721         result = CURLE_FAILED_INIT;
2722     }
2723     else {
2724       if(global->libcurl) {
2725         /* Initialise the libcurl source output */
2726         result = easysrc_init();
2727       }
2728 
2729       /* Perform the main operations */
2730       if(!result) {
2731         size_t count = 0;
2732         struct OperationConfig *operation = global->first;
2733         CURLSH *share = curl_share_init();
2734         if(!share) {
2735           if(global->libcurl) {
2736             /* Cleanup the libcurl source output */
2737             easysrc_cleanup();
2738           }
2739           return CURLE_OUT_OF_MEMORY;
2740         }
2741 
2742         curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
2743         curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
2744         curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
2745         curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
2746         curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
2747         curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
2748 
2749         /* Get the required arguments for each operation */
2750         do {
2751           result = get_args(operation, count++);
2752 
2753           operation = operation->next;
2754         } while(!result && operation);
2755 
2756         /* Set the current operation pointer */
2757         global->current = global->first;
2758 
2759         /* now run! */
2760         result = run_all_transfers(global, share, result);
2761 
2762         curl_share_cleanup(share);
2763         if(global->libcurl) {
2764           /* Cleanup the libcurl source output */
2765           easysrc_cleanup();
2766 
2767           /* Dump the libcurl code if previously enabled */
2768           dumpeasysrc(global);
2769         }
2770       }
2771       else
2772         errorf(global, "out of memory\n");
2773     }
2774   }
2775 
2776   return result;
2777 }
2778