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