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