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