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