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