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