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