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