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