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 *retryp = FALSE;
383 *delay = 0; /* for no retry, keep it zero */
384
385 if(!curl || !config)
386 return result;
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 if((retry_after * 1000) > sleeptime)
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, "ab");
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 = (unsigned int)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 #ifndef DEBUGBUILD
1278 /* On most modern OSes, exiting works thoroughly,
1279 we'll clean everything up via exit(), so don't bother with
1280 slow cleanups. Crappy ones might need to skip this.
1281 Note: avoid having this setopt added to the --libcurl source
1282 output. */
1283 result = curl_easy_setopt(curl, CURLOPT_QUICK_EXIT, 1L);
1284 if(result)
1285 break;
1286 #endif
1287
1288 if(!config->tcp_nodelay)
1289 my_setopt(curl, CURLOPT_TCP_NODELAY, 0L);
1290
1291 if(config->tcp_fastopen)
1292 my_setopt(curl, CURLOPT_TCP_FASTOPEN, 1L);
1293
1294 /* where to store */
1295 my_setopt(curl, CURLOPT_WRITEDATA, per);
1296 my_setopt(curl, CURLOPT_INTERLEAVEDATA, per);
1297
1298 /* what call to write */
1299 my_setopt(curl, CURLOPT_WRITEFUNCTION, tool_write_cb);
1300
1301 /* Note that if CURLOPT_READFUNCTION is fread (the default), then
1302 * lib/telnet.c will Curl_poll() on the input file descriptor
1303 * rather than calling the READFUNCTION at regular intervals.
1304 * The circumstances in which it is preferable to enable this
1305 * behavior, by omitting to set the READFUNCTION & READDATA options,
1306 * have not been determined.
1307 */
1308 my_setopt(curl, CURLOPT_READDATA, per);
1309 /* what call to read */
1310 my_setopt(curl, CURLOPT_READFUNCTION, tool_read_cb);
1311
1312 /* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what
1313 CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */
1314 my_setopt(curl, CURLOPT_SEEKDATA, per);
1315 my_setopt(curl, CURLOPT_SEEKFUNCTION, tool_seek_cb);
1316
1317 {
1318 #ifdef CURLDEBUG
1319 char *env = getenv("CURL_BUFFERSIZE");
1320 if(env) {
1321 long size = strtol(env, NULL, 10);
1322 if(size)
1323 my_setopt(curl, CURLOPT_BUFFERSIZE, size);
1324 }
1325 else
1326 #endif
1327 if(config->recvpersecond &&
1328 (config->recvpersecond < BUFFER_SIZE))
1329 /* use a smaller sized buffer for better sleeps */
1330 my_setopt(curl, CURLOPT_BUFFERSIZE, (long)config->recvpersecond);
1331 else
1332 my_setopt(curl, CURLOPT_BUFFERSIZE, (long)BUFFER_SIZE);
1333 }
1334
1335 my_setopt_str(curl, CURLOPT_URL, per->this_url);
1336 my_setopt(curl, CURLOPT_NOPROGRESS,
1337 global->noprogress || global->silent?1L:0L);
1338 if(config->no_body)
1339 my_setopt(curl, CURLOPT_NOBODY, 1L);
1340
1341 if(config->oauth_bearer)
1342 my_setopt_str(curl, CURLOPT_XOAUTH2_BEARER, config->oauth_bearer);
1343
1344 my_setopt_str(curl, CURLOPT_PROXY, config->proxy);
1345
1346 if(config->proxy && result) {
1347 errorf(global, "proxy support is disabled in this libcurl");
1348 config->synthetic_error = TRUE;
1349 result = CURLE_NOT_BUILT_IN;
1350 break;
1351 }
1352
1353 /* new in libcurl 7.5 */
1354 if(config->proxy)
1355 my_setopt_enum(curl, CURLOPT_PROXYTYPE, config->proxyver);
1356
1357 my_setopt_str(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd);
1358
1359 /* new in libcurl 7.3 */
1360 my_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel?1L:0L);
1361
1362 /* new in libcurl 7.52.0 */
1363 if(config->preproxy)
1364 my_setopt_str(curl, CURLOPT_PRE_PROXY, config->preproxy);
1365
1366 /* new in libcurl 7.10.6 */
1367 if(config->proxyanyauth)
1368 my_setopt_bitmask(curl, CURLOPT_PROXYAUTH,
1369 (long)CURLAUTH_ANY);
1370 else if(config->proxynegotiate)
1371 my_setopt_bitmask(curl, CURLOPT_PROXYAUTH,
1372 (long)CURLAUTH_GSSNEGOTIATE);
1373 else if(config->proxyntlm)
1374 my_setopt_bitmask(curl, CURLOPT_PROXYAUTH,
1375 (long)CURLAUTH_NTLM);
1376 else if(config->proxydigest)
1377 my_setopt_bitmask(curl, CURLOPT_PROXYAUTH,
1378 (long)CURLAUTH_DIGEST);
1379 else if(config->proxybasic)
1380 my_setopt_bitmask(curl, CURLOPT_PROXYAUTH,
1381 (long)CURLAUTH_BASIC);
1382
1383 /* new in libcurl 7.19.4 */
1384 my_setopt_str(curl, CURLOPT_NOPROXY, config->noproxy);
1385
1386 my_setopt(curl, CURLOPT_SUPPRESS_CONNECT_HEADERS,
1387 config->suppress_connect_headers?1L:0L);
1388
1389 my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror?1L:0L);
1390 my_setopt(curl, CURLOPT_REQUEST_TARGET, config->request_target);
1391 my_setopt(curl, CURLOPT_UPLOAD, per->uploadfile?1L:0L);
1392 my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly?1L:0L);
1393 my_setopt(curl, CURLOPT_APPEND, config->ftp_append?1L:0L);
1394
1395 if(config->netrc_opt)
1396 my_setopt_enum(curl, CURLOPT_NETRC, (long)CURL_NETRC_OPTIONAL);
1397 else if(config->netrc || config->netrc_file)
1398 my_setopt_enum(curl, CURLOPT_NETRC, (long)CURL_NETRC_REQUIRED);
1399 else
1400 my_setopt_enum(curl, CURLOPT_NETRC, (long)CURL_NETRC_IGNORED);
1401
1402 if(config->netrc_file)
1403 my_setopt_str(curl, CURLOPT_NETRC_FILE, config->netrc_file);
1404
1405 my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii?1L:0L);
1406 if(config->login_options)
1407 my_setopt_str(curl, CURLOPT_LOGIN_OPTIONS, config->login_options);
1408 my_setopt_str(curl, CURLOPT_USERPWD, config->userpwd);
1409 my_setopt_str(curl, CURLOPT_RANGE, config->range);
1410 if(!global->parallel) {
1411 per->errorbuffer = global_errorbuffer;
1412 my_setopt(curl, CURLOPT_ERRORBUFFER, global_errorbuffer);
1413 }
1414 my_setopt(curl, CURLOPT_TIMEOUT_MS, config->timeout_ms);
1415
1416 switch(config->httpreq) {
1417 case HTTPREQ_SIMPLEPOST:
1418 if(config->resume_from) {
1419 errorf(global, "cannot mix --continue-at with --data");
1420 result = CURLE_FAILED_INIT;
1421 }
1422 else {
1423 my_setopt_str(curl, CURLOPT_POSTFIELDS,
1424 curlx_dyn_ptr(&config->postdata));
1425 my_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE,
1426 (curl_off_t)curlx_dyn_len(&config->postdata));
1427 }
1428 break;
1429 case HTTPREQ_MIMEPOST:
1430 /* free previous remainders */
1431 curl_mime_free(config->mimepost);
1432 config->mimepost = NULL;
1433 if(config->resume_from) {
1434 errorf(global, "cannot mix --continue-at with --form");
1435 result = CURLE_FAILED_INIT;
1436 }
1437 else {
1438 result = tool2curlmime(curl, config->mimeroot, &config->mimepost);
1439 if(!result)
1440 my_setopt_mimepost(curl, CURLOPT_MIMEPOST, config->mimepost);
1441 }
1442 break;
1443 default:
1444 break;
1445 }
1446 if(result)
1447 break;
1448
1449 /* new in libcurl 7.81.0 */
1450 if(config->mime_options)
1451 my_setopt(curl, CURLOPT_MIME_OPTIONS, config->mime_options);
1452
1453 /* new in libcurl 7.10.6 (default is Basic) */
1454 if(config->authtype)
1455 my_setopt_bitmask(curl, CURLOPT_HTTPAUTH, (long)config->authtype);
1456
1457 my_setopt_slist(curl, CURLOPT_HTTPHEADER, config->headers);
1458
1459 if(proto_http || proto_rtsp) {
1460 my_setopt_str(curl, CURLOPT_REFERER, config->referer);
1461 my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent);
1462 }
1463
1464 if(proto_http) {
1465 long postRedir = 0;
1466
1467 my_setopt(curl, CURLOPT_FOLLOWLOCATION,
1468 config->followlocation?1L:0L);
1469 my_setopt(curl, CURLOPT_UNRESTRICTED_AUTH,
1470 config->unrestricted_auth?1L:0L);
1471 my_setopt_str(curl, CURLOPT_AWS_SIGV4, config->aws_sigv4);
1472 my_setopt(curl, CURLOPT_AUTOREFERER, config->autoreferer?1L:0L);
1473
1474 /* new in libcurl 7.36.0 */
1475 if(config->proxyheaders) {
1476 my_setopt_slist(curl, CURLOPT_PROXYHEADER, config->proxyheaders);
1477 my_setopt(curl, CURLOPT_HEADEROPT, (long)CURLHEADER_SEPARATE);
1478 }
1479
1480 /* new in libcurl 7.5 */
1481 my_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs);
1482
1483 if(config->httpversion)
1484 my_setopt_enum(curl, CURLOPT_HTTP_VERSION, config->httpversion);
1485 else if(feature_http2)
1486 my_setopt_enum(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
1487
1488 /* curl 7.19.1 (the 301 version existed in 7.18.2),
1489 303 was added in 7.26.0 */
1490 if(config->post301)
1491 postRedir |= CURL_REDIR_POST_301;
1492 if(config->post302)
1493 postRedir |= CURL_REDIR_POST_302;
1494 if(config->post303)
1495 postRedir |= CURL_REDIR_POST_303;
1496 my_setopt(curl, CURLOPT_POSTREDIR, postRedir);
1497
1498 /* new in libcurl 7.21.6 */
1499 if(config->encoding)
1500 my_setopt_str(curl, CURLOPT_ACCEPT_ENCODING, "");
1501
1502 /* new in libcurl 7.21.6 */
1503 if(config->tr_encoding)
1504 my_setopt(curl, CURLOPT_TRANSFER_ENCODING, 1L);
1505 /* new in libcurl 7.64.0 */
1506 my_setopt(curl, CURLOPT_HTTP09_ALLOWED,
1507 config->http09_allowed ? 1L : 0L);
1508 if(result) {
1509 errorf(global, "HTTP/0.9 is not supported in this build");
1510 return result;
1511 }
1512
1513 } /* (proto_http) */
1514
1515 if(proto_ftp)
1516 my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport);
1517 my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
1518 config->low_speed_limit);
1519 my_setopt(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time);
1520 my_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE,
1521 config->sendpersecond);
1522 my_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE,
1523 config->recvpersecond);
1524
1525 if(config->use_resume)
1526 my_setopt(curl, CURLOPT_RESUME_FROM_LARGE, config->resume_from);
1527 else
1528 my_setopt(curl, CURLOPT_RESUME_FROM_LARGE, CURL_OFF_T_C(0));
1529
1530 my_setopt_str(curl, CURLOPT_KEYPASSWD, config->key_passwd);
1531 my_setopt_str(curl, CURLOPT_PROXY_KEYPASSWD, config->proxy_key_passwd);
1532
1533 if(use_proto == proto_scp || use_proto == proto_sftp) {
1534 /* SSH and SSL private key uses same command-line option */
1535 /* new in libcurl 7.16.1 */
1536 my_setopt_str(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key);
1537 /* new in libcurl 7.16.1 */
1538 my_setopt_str(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey);
1539
1540 /* new in libcurl 7.17.1: SSH host key md5 checking allows us
1541 to fail if we are not talking to who we think we should */
1542 my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5,
1543 config->hostpubmd5);
1544
1545 /* new in libcurl 7.80.0: SSH host key sha256 checking allows us
1546 to fail if we are not talking to who we think we should */
1547 my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256,
1548 config->hostpubsha256);
1549
1550 /* new in libcurl 7.56.0 */
1551 if(config->ssh_compression)
1552 my_setopt(curl, CURLOPT_SSH_COMPRESSION, 1L);
1553 }
1554
1555 if(config->cacert)
1556 my_setopt_str(curl, CURLOPT_CAINFO, config->cacert);
1557 if(config->proxy_cacert)
1558 my_setopt_str(curl, CURLOPT_PROXY_CAINFO, config->proxy_cacert);
1559
1560 if(config->capath) {
1561 result = res_setopt_str(curl, CURLOPT_CAPATH, config->capath);
1562 if(result == CURLE_NOT_BUILT_IN) {
1563 warnf(global, "ignoring %s, not supported by libcurl",
1564 capath_from_env?
1565 "SSL_CERT_DIR environment variable":"--capath");
1566 }
1567 else if(result)
1568 break;
1569 }
1570 /* For the time being if --proxy-capath is not set then we use the
1571 --capath value for it, if any. See #1257 */
1572 if(config->proxy_capath || config->capath) {
1573 result = res_setopt_str(curl, CURLOPT_PROXY_CAPATH,
1574 (config->proxy_capath ?
1575 config->proxy_capath :
1576 config->capath));
1577 if((result == CURLE_NOT_BUILT_IN) ||
1578 (result == CURLE_UNKNOWN_OPTION)) {
1579 if(config->proxy_capath) {
1580 warnf(global,
1581 "ignoring --proxy-capath, not supported by libcurl");
1582 }
1583 }
1584 else if(result)
1585 break;
1586 }
1587
1588 if(config->crlfile)
1589 my_setopt_str(curl, CURLOPT_CRLFILE, config->crlfile);
1590 if(config->proxy_crlfile)
1591 my_setopt_str(curl, CURLOPT_PROXY_CRLFILE, config->proxy_crlfile);
1592 else if(config->crlfile) /* CURLOPT_PROXY_CRLFILE default is crlfile */
1593 my_setopt_str(curl, CURLOPT_PROXY_CRLFILE, config->crlfile);
1594
1595 if(config->pinnedpubkey)
1596 my_setopt_str(curl, CURLOPT_PINNEDPUBLICKEY, config->pinnedpubkey);
1597
1598 if(config->ssl_ec_curves)
1599 my_setopt_str(curl, CURLOPT_SSL_EC_CURVES, config->ssl_ec_curves);
1600
1601 if(config->writeout)
1602 my_setopt_str(curl, CURLOPT_CERTINFO, 1L);
1603
1604 if(feature_ssl) {
1605 /* Check if config->cert is a PKCS#11 URI and set the
1606 * config->cert_type if necessary */
1607 if(config->cert) {
1608 if(!config->cert_type) {
1609 if(is_pkcs11_uri(config->cert)) {
1610 config->cert_type = strdup("ENG");
1611 }
1612 }
1613 }
1614
1615 /* Check if config->key is a PKCS#11 URI and set the
1616 * config->key_type if necessary */
1617 if(config->key) {
1618 if(!config->key_type) {
1619 if(is_pkcs11_uri(config->key)) {
1620 config->key_type = strdup("ENG");
1621 }
1622 }
1623 }
1624
1625 /* Check if config->proxy_cert is a PKCS#11 URI and set the
1626 * config->proxy_type if necessary */
1627 if(config->proxy_cert) {
1628 if(!config->proxy_cert_type) {
1629 if(is_pkcs11_uri(config->proxy_cert)) {
1630 config->proxy_cert_type = strdup("ENG");
1631 }
1632 }
1633 }
1634
1635 /* Check if config->proxy_key is a PKCS#11 URI and set the
1636 * config->proxy_key_type if necessary */
1637 if(config->proxy_key) {
1638 if(!config->proxy_key_type) {
1639 if(is_pkcs11_uri(config->proxy_key)) {
1640 config->proxy_key_type = strdup("ENG");
1641 }
1642 }
1643 }
1644
1645 /* In debug build of curl tool, using
1646 * --cert loadmem=<filename>:<password> --cert-type p12
1647 * must do the same thing as classic:
1648 * --cert <filename>:<password> --cert-type p12
1649 * but is designed to test blob */
1650 #if defined(CURLDEBUG) || defined(DEBUGBUILD)
1651 if(config->cert && (strlen(config->cert) > 8) &&
1652 (memcmp(config->cert, "loadmem=",8) == 0)) {
1653 FILE *fInCert = fopen(config->cert + 8, "rb");
1654 void *certdata = NULL;
1655 long filesize = 0;
1656 bool continue_reading = fInCert != NULL;
1657 if(continue_reading)
1658 continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
1659 if(continue_reading)
1660 filesize = ftell(fInCert);
1661 if(filesize < 0)
1662 continue_reading = FALSE;
1663 if(continue_reading)
1664 continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
1665 if(continue_reading)
1666 certdata = malloc(((size_t)filesize) + 1);
1667 if((!certdata) ||
1668 ((int)fread(certdata, (size_t)filesize, 1, fInCert) != 1))
1669 continue_reading = FALSE;
1670 if(fInCert)
1671 fclose(fInCert);
1672 if((filesize > 0) && continue_reading) {
1673 struct curl_blob structblob;
1674 structblob.data = certdata;
1675 structblob.len = (size_t)filesize;
1676 structblob.flags = CURL_BLOB_COPY;
1677 my_setopt_str(curl, CURLOPT_SSLCERT_BLOB, &structblob);
1678 /* if test run well, we are sure we don't reuse
1679 * original mem pointer */
1680 memset(certdata, 0, (size_t)filesize);
1681 }
1682 free(certdata);
1683 }
1684 else
1685 #endif
1686 my_setopt_str(curl, CURLOPT_SSLCERT, config->cert);
1687 my_setopt_str(curl, CURLOPT_PROXY_SSLCERT, config->proxy_cert);
1688 my_setopt_str(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
1689 my_setopt_str(curl, CURLOPT_PROXY_SSLCERTTYPE,
1690 config->proxy_cert_type);
1691
1692
1693 #if defined(CURLDEBUG) || defined(DEBUGBUILD)
1694 if(config->key && (strlen(config->key) > 8) &&
1695 (memcmp(config->key, "loadmem=",8) == 0)) {
1696 FILE *fInCert = fopen(config->key + 8, "rb");
1697 void *certdata = NULL;
1698 long filesize = 0;
1699 bool continue_reading = fInCert != NULL;
1700 if(continue_reading)
1701 continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
1702 if(continue_reading)
1703 filesize = ftell(fInCert);
1704 if(filesize < 0)
1705 continue_reading = FALSE;
1706 if(continue_reading)
1707 continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
1708 if(continue_reading)
1709 certdata = malloc(((size_t)filesize) + 1);
1710 if((!certdata) ||
1711 ((int)fread(certdata, (size_t)filesize, 1, fInCert) != 1))
1712 continue_reading = FALSE;
1713 if(fInCert)
1714 fclose(fInCert);
1715 if((filesize > 0) && continue_reading) {
1716 struct curl_blob structblob;
1717 structblob.data = certdata;
1718 structblob.len = (size_t)filesize;
1719 structblob.flags = CURL_BLOB_COPY;
1720 my_setopt_str(curl, CURLOPT_SSLKEY_BLOB, &structblob);
1721 /* if test run well, we are sure we don't reuse
1722 * original mem pointer */
1723 memset(certdata, 0, (size_t)filesize);
1724 }
1725 free(certdata);
1726 }
1727 else
1728 #endif
1729 my_setopt_str(curl, CURLOPT_SSLKEY, config->key);
1730 my_setopt_str(curl, CURLOPT_PROXY_SSLKEY, config->proxy_key);
1731 my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type);
1732 my_setopt_str(curl, CURLOPT_PROXY_SSLKEYTYPE,
1733 config->proxy_key_type);
1734 if(config->insecure_ok) {
1735 my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
1736 my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
1737 }
1738 else {
1739 my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
1740 /* libcurl default is strict verifyhost -> 2L */
1741 /* my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); */
1742 }
1743
1744 if(config->doh_insecure_ok) {
1745 my_setopt(curl, CURLOPT_DOH_SSL_VERIFYPEER, 0L);
1746 my_setopt(curl, CURLOPT_DOH_SSL_VERIFYHOST, 0L);
1747 }
1748
1749 if(config->proxy_insecure_ok) {
1750 my_setopt(curl, CURLOPT_PROXY_SSL_VERIFYPEER, 0L);
1751 my_setopt(curl, CURLOPT_PROXY_SSL_VERIFYHOST, 0L);
1752 }
1753 else {
1754 my_setopt(curl, CURLOPT_PROXY_SSL_VERIFYPEER, 1L);
1755 }
1756
1757 if(config->verifystatus)
1758 my_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
1759
1760 if(config->doh_verifystatus)
1761 my_setopt(curl, CURLOPT_DOH_SSL_VERIFYSTATUS, 1L);
1762
1763 if(config->falsestart)
1764 my_setopt(curl, CURLOPT_SSL_FALSESTART, 1L);
1765
1766 my_setopt_SSLVERSION(curl, CURLOPT_SSLVERSION,
1767 config->ssl_version | config->ssl_version_max);
1768 if(config->proxy)
1769 my_setopt_SSLVERSION(curl, CURLOPT_PROXY_SSLVERSION,
1770 config->proxy_ssl_version);
1771
1772 {
1773 long mask =
1774 (config->ssl_allow_beast ?
1775 CURLSSLOPT_ALLOW_BEAST : 0) |
1776 (config->ssl_no_revoke ?
1777 CURLSSLOPT_NO_REVOKE : 0) |
1778 (config->ssl_revoke_best_effort ?
1779 CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
1780 (config->native_ca_store ?
1781 CURLSSLOPT_NATIVE_CA : 0) |
1782 (config->ssl_auto_client_cert ?
1783 CURLSSLOPT_AUTO_CLIENT_CERT : 0);
1784
1785 if(mask)
1786 my_setopt_bitmask(curl, CURLOPT_SSL_OPTIONS, mask);
1787 }
1788
1789 {
1790 long mask =
1791 (config->proxy_ssl_allow_beast ?
1792 CURLSSLOPT_ALLOW_BEAST : 0) |
1793 (config->proxy_ssl_auto_client_cert ?
1794 CURLSSLOPT_AUTO_CLIENT_CERT : 0) |
1795 (config->proxy_native_ca_store ?
1796 CURLSSLOPT_NATIVE_CA : 0);
1797
1798 if(mask)
1799 my_setopt_bitmask(curl, CURLOPT_PROXY_SSL_OPTIONS, mask);
1800 }
1801 }
1802
1803 if(config->path_as_is)
1804 my_setopt(curl, CURLOPT_PATH_AS_IS, 1L);
1805
1806 if((use_proto == proto_scp || use_proto == proto_sftp) &&
1807 !config->insecure_ok) {
1808 char *known = findfile(".ssh/known_hosts", FALSE);
1809 if(known) {
1810 /* new in curl 7.19.6 */
1811 result = res_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, known);
1812 curl_free(known);
1813 if(result == CURLE_UNKNOWN_OPTION)
1814 /* libssh2 version older than 1.1.1 */
1815 result = CURLE_OK;
1816 if(result)
1817 break;
1818 }
1819 else
1820 warnf(global, "Couldn't find a known_hosts file");
1821 }
1822
1823 if(config->no_body || config->remote_time) {
1824 /* no body or use remote time */
1825 my_setopt(curl, CURLOPT_FILETIME, 1L);
1826 }
1827
1828 my_setopt(curl, CURLOPT_CRLF, config->crlf?1L:0L);
1829 my_setopt_slist(curl, CURLOPT_QUOTE, config->quote);
1830 my_setopt_slist(curl, CURLOPT_POSTQUOTE, config->postquote);
1831 my_setopt_slist(curl, CURLOPT_PREQUOTE, config->prequote);
1832
1833 if(config->cookies) {
1834 struct curlx_dynbuf cookies;
1835 struct curl_slist *cl;
1836
1837 /* The maximum size needs to match MAX_NAME in cookie.h */
1838 #define MAX_COOKIE_LINE 8200
1839 curlx_dyn_init(&cookies, MAX_COOKIE_LINE);
1840 for(cl = config->cookies; cl; cl = cl->next) {
1841 if(cl == config->cookies)
1842 result = curlx_dyn_addf(&cookies, "%s", cl->data);
1843 else
1844 result = curlx_dyn_addf(&cookies, ";%s", cl->data);
1845
1846 if(result) {
1847 warnf(global,
1848 "skipped provided cookie, the cookie header "
1849 "would go over %u bytes", MAX_COOKIE_LINE);
1850 break;
1851 }
1852 }
1853
1854 my_setopt_str(curl, CURLOPT_COOKIE, curlx_dyn_ptr(&cookies));
1855 curlx_dyn_free(&cookies);
1856 }
1857
1858 if(config->cookiefiles) {
1859 struct curl_slist *cfl;
1860
1861 for(cfl = config->cookiefiles; cfl; cfl = cfl->next)
1862 my_setopt_str(curl, CURLOPT_COOKIEFILE, cfl->data);
1863 }
1864
1865 /* new in libcurl 7.9 */
1866 if(config->cookiejar)
1867 my_setopt_str(curl, CURLOPT_COOKIEJAR, config->cookiejar);
1868
1869 /* new in libcurl 7.9.7 */
1870 my_setopt(curl, CURLOPT_COOKIESESSION, config->cookiesession?1L:0L);
1871
1872 my_setopt_enum(curl, CURLOPT_TIMECONDITION, (long)config->timecond);
1873 my_setopt(curl, CURLOPT_TIMEVALUE_LARGE, config->condtime);
1874 my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest);
1875 customrequest_helper(config, config->httpreq, config->customrequest);
1876 my_setopt(curl, CURLOPT_STDERR, tool_stderr);
1877
1878 /* three new ones in libcurl 7.3: */
1879 my_setopt_str(curl, CURLOPT_INTERFACE, config->iface);
1880 my_setopt_str(curl, CURLOPT_KRBLEVEL, config->krblevel);
1881 progressbarinit(&per->progressbar, config);
1882
1883 if((global->progressmode == CURL_PROGRESS_BAR) &&
1884 !global->noprogress && !global->silent) {
1885 /* we want the alternative style, then we have to implement it
1886 ourselves! */
1887 my_setopt(curl, CURLOPT_XFERINFOFUNCTION, tool_progress_cb);
1888 my_setopt(curl, CURLOPT_XFERINFODATA, per);
1889 }
1890 else if(per->uploadfile && !strcmp(per->uploadfile, ".")) {
1891 /* when reading from stdin in non-blocking mode, we use the progress
1892 function to unpause a busy read */
1893 my_setopt(curl, CURLOPT_NOPROGRESS, 0L);
1894 my_setopt(curl, CURLOPT_XFERINFOFUNCTION, tool_readbusy_cb);
1895 my_setopt(curl, CURLOPT_XFERINFODATA, per);
1896 }
1897
1898 /* new in libcurl 7.24.0: */
1899 if(config->dns_servers)
1900 my_setopt_str(curl, CURLOPT_DNS_SERVERS, config->dns_servers);
1901
1902 /* new in libcurl 7.33.0: */
1903 if(config->dns_interface)
1904 my_setopt_str(curl, CURLOPT_DNS_INTERFACE, config->dns_interface);
1905 if(config->dns_ipv4_addr)
1906 my_setopt_str(curl, CURLOPT_DNS_LOCAL_IP4, config->dns_ipv4_addr);
1907 if(config->dns_ipv6_addr)
1908 my_setopt_str(curl, CURLOPT_DNS_LOCAL_IP6, config->dns_ipv6_addr);
1909
1910 /* new in libcurl 7.6.2: */
1911 my_setopt_slist(curl, CURLOPT_TELNETOPTIONS, config->telnet_options);
1912
1913 /* new in libcurl 7.7: */
1914 my_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, config->connecttimeout_ms);
1915
1916 if(config->doh_url)
1917 my_setopt_str(curl, CURLOPT_DOH_URL, config->doh_url);
1918
1919 if(config->cipher_list)
1920 my_setopt_str(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list);
1921
1922 if(config->proxy_cipher_list)
1923 my_setopt_str(curl, CURLOPT_PROXY_SSL_CIPHER_LIST,
1924 config->proxy_cipher_list);
1925
1926 if(config->cipher13_list)
1927 my_setopt_str(curl, CURLOPT_TLS13_CIPHERS, config->cipher13_list);
1928
1929 if(config->proxy_cipher13_list)
1930 my_setopt_str(curl, CURLOPT_PROXY_TLS13_CIPHERS,
1931 config->proxy_cipher13_list);
1932
1933 /* new in libcurl 7.9.2: */
1934 if(config->disable_epsv)
1935 /* disable it */
1936 my_setopt(curl, CURLOPT_FTP_USE_EPSV, 0L);
1937
1938 /* new in libcurl 7.10.5 */
1939 if(config->disable_eprt)
1940 /* disable it */
1941 my_setopt(curl, CURLOPT_FTP_USE_EPRT, 0L);
1942
1943 if(global->tracetype != TRACE_NONE) {
1944 my_setopt(curl, CURLOPT_DEBUGFUNCTION, tool_debug_cb);
1945 my_setopt(curl, CURLOPT_DEBUGDATA, config);
1946 my_setopt(curl, CURLOPT_VERBOSE, 1L);
1947 }
1948
1949 /* new in curl 7.9.3 */
1950 if(config->engine) {
1951 result = res_setopt_str(curl, CURLOPT_SSLENGINE, config->engine);
1952 if(result)
1953 break;
1954 }
1955
1956 /* new in curl 7.10.7, extended in 7.19.4. Modified to use
1957 CREATE_DIR_RETRY in 7.49.0 */
1958 my_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS,
1959 (long)(config->ftp_create_dirs?
1960 CURLFTP_CREATE_DIR_RETRY:
1961 CURLFTP_CREATE_DIR_NONE));
1962
1963 /* new in curl 7.10.8 */
1964 if(config->max_filesize)
1965 my_setopt(curl, CURLOPT_MAXFILESIZE_LARGE,
1966 config->max_filesize);
1967
1968 my_setopt(curl, CURLOPT_IPRESOLVE, config->ip_version);
1969
1970 /* new in curl 7.15.5 */
1971 if(config->ftp_ssl_reqd)
1972 my_setopt_enum(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
1973
1974 /* new in curl 7.11.0 */
1975 else if(config->ftp_ssl)
1976 my_setopt_enum(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_TRY);
1977
1978 /* new in curl 7.16.0 */
1979 else if(config->ftp_ssl_control)
1980 my_setopt_enum(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_CONTROL);
1981
1982 /* new in curl 7.16.1 */
1983 if(config->ftp_ssl_ccc)
1984 my_setopt_enum(curl, CURLOPT_FTP_SSL_CCC,
1985 (long)config->ftp_ssl_ccc_mode);
1986
1987 /* new in curl 7.19.4 */
1988 if(config->socks5_gssapi_nec)
1989 my_setopt_str(curl, CURLOPT_SOCKS5_GSSAPI_NEC, 1L);
1990
1991 /* new in curl 7.55.0 */
1992 if(config->socks5_auth)
1993 my_setopt_bitmask(curl, CURLOPT_SOCKS5_AUTH,
1994 (long)config->socks5_auth);
1995
1996 /* new in curl 7.43.0 */
1997 if(config->proxy_service_name)
1998 my_setopt_str(curl, CURLOPT_PROXY_SERVICE_NAME,
1999 config->proxy_service_name);
2000
2001 /* new in curl 7.43.0 */
2002 if(config->service_name)
2003 my_setopt_str(curl, CURLOPT_SERVICE_NAME,
2004 config->service_name);
2005
2006 /* curl 7.13.0 */
2007 my_setopt_str(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account);
2008 my_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl?1L:0L);
2009
2010 /* curl 7.14.2 */
2011 my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip?1L:0L);
2012
2013 /* curl 7.15.1 */
2014 if(proto_ftp)
2015 my_setopt(curl, CURLOPT_FTP_FILEMETHOD,
2016 (long)config->ftp_filemethod);
2017
2018 /* curl 7.15.2 */
2019 if(config->localport) {
2020 my_setopt(curl, CURLOPT_LOCALPORT, config->localport);
2021 my_setopt_str(curl, CURLOPT_LOCALPORTRANGE, config->localportrange);
2022 }
2023
2024 /* curl 7.15.5 */
2025 my_setopt_str(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER,
2026 config->ftp_alternative_to_user);
2027
2028 /* curl 7.16.0 */
2029 if(config->disable_sessionid)
2030 /* disable it */
2031 my_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, 0L);
2032
2033 /* curl 7.16.2 */
2034 if(config->raw) {
2035 my_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, 0L);
2036 my_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, 0L);
2037 }
2038
2039 /* curl 7.17.1 */
2040 if(!config->nokeepalive) {
2041 my_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
2042 if(config->alivetime) {
2043 my_setopt(curl, CURLOPT_TCP_KEEPIDLE, config->alivetime);
2044 my_setopt(curl, CURLOPT_TCP_KEEPINTVL, config->alivetime);
2045 }
2046 }
2047 else
2048 my_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L);
2049
2050 /* curl 7.20.0 */
2051 if(config->tftp_blksize && proto_tftp)
2052 my_setopt(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize);
2053
2054 if(config->mail_from)
2055 my_setopt_str(curl, CURLOPT_MAIL_FROM, config->mail_from);
2056
2057 if(config->mail_rcpt)
2058 my_setopt_slist(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt);
2059
2060 /* curl 7.69.x */
2061 my_setopt(curl, CURLOPT_MAIL_RCPT_ALLOWFAILS,
2062 config->mail_rcpt_allowfails ? 1L : 0L);
2063
2064 /* curl 7.20.x */
2065 if(config->ftp_pret)
2066 my_setopt(curl, CURLOPT_FTP_USE_PRET, 1L);
2067
2068 if(config->create_file_mode)
2069 my_setopt(curl, CURLOPT_NEW_FILE_PERMS, config->create_file_mode);
2070
2071 if(config->proto_present)
2072 my_setopt_str(curl, CURLOPT_PROTOCOLS_STR, config->proto_str);
2073 if(config->proto_redir_present)
2074 my_setopt_str(curl, CURLOPT_REDIR_PROTOCOLS_STR,
2075 config->proto_redir_str);
2076
2077 if(config->content_disposition
2078 && (urlnode->flags & GETOUT_USEREMOTE))
2079 hdrcbdata->honor_cd_filename = TRUE;
2080 else
2081 hdrcbdata->honor_cd_filename = FALSE;
2082
2083 hdrcbdata->outs = outs;
2084 hdrcbdata->heads = heads;
2085 hdrcbdata->etag_save = etag_save;
2086 hdrcbdata->global = global;
2087 hdrcbdata->config = config;
2088
2089 my_setopt(curl, CURLOPT_HEADERFUNCTION, tool_header_cb);
2090 my_setopt(curl, CURLOPT_HEADERDATA, per);
2091
2092 if(config->resolve)
2093 /* new in 7.21.3 */
2094 my_setopt_slist(curl, CURLOPT_RESOLVE, config->resolve);
2095
2096 if(config->connect_to)
2097 /* new in 7.49.0 */
2098 my_setopt_slist(curl, CURLOPT_CONNECT_TO, config->connect_to);
2099
2100 /* new in 7.21.4 */
2101 if(feature_tls_srp) {
2102 if(config->tls_username)
2103 my_setopt_str(curl, CURLOPT_TLSAUTH_USERNAME,
2104 config->tls_username);
2105 if(config->tls_password)
2106 my_setopt_str(curl, CURLOPT_TLSAUTH_PASSWORD,
2107 config->tls_password);
2108 if(config->tls_authtype)
2109 my_setopt_str(curl, CURLOPT_TLSAUTH_TYPE,
2110 config->tls_authtype);
2111 if(config->proxy_tls_username)
2112 my_setopt_str(curl, CURLOPT_PROXY_TLSAUTH_USERNAME,
2113 config->proxy_tls_username);
2114 if(config->proxy_tls_password)
2115 my_setopt_str(curl, CURLOPT_PROXY_TLSAUTH_PASSWORD,
2116 config->proxy_tls_password);
2117 if(config->proxy_tls_authtype)
2118 my_setopt_str(curl, CURLOPT_PROXY_TLSAUTH_TYPE,
2119 config->proxy_tls_authtype);
2120 }
2121
2122 /* new in 7.22.0 */
2123 if(config->gssapi_delegation)
2124 my_setopt_str(curl, CURLOPT_GSSAPI_DELEGATION,
2125 config->gssapi_delegation);
2126
2127 if(config->mail_auth)
2128 my_setopt_str(curl, CURLOPT_MAIL_AUTH, config->mail_auth);
2129
2130 /* new in 7.66.0 */
2131 if(config->sasl_authzid)
2132 my_setopt_str(curl, CURLOPT_SASL_AUTHZID, config->sasl_authzid);
2133
2134 /* new in 7.31.0 */
2135 if(config->sasl_ir)
2136 my_setopt(curl, CURLOPT_SASL_IR, 1L);
2137
2138 if(config->noalpn) {
2139 my_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, 0L);
2140 }
2141
2142 /* new in 7.40.0, abstract support added in 7.53.0 */
2143 if(config->unix_socket_path) {
2144 if(config->abstract_unix_socket) {
2145 my_setopt_str(curl, CURLOPT_ABSTRACT_UNIX_SOCKET,
2146 config->unix_socket_path);
2147 }
2148 else {
2149 my_setopt_str(curl, CURLOPT_UNIX_SOCKET_PATH,
2150 config->unix_socket_path);
2151 }
2152 }
2153
2154 /* new in 7.45.0 */
2155 if(config->proto_default)
2156 my_setopt_str(curl, CURLOPT_DEFAULT_PROTOCOL, config->proto_default);
2157
2158 /* new in 7.47.0 */
2159 if(config->expect100timeout_ms > 0)
2160 my_setopt_str(curl, CURLOPT_EXPECT_100_TIMEOUT_MS,
2161 config->expect100timeout_ms);
2162
2163 /* new in 7.48.0 */
2164 if(config->tftp_no_options && proto_tftp)
2165 my_setopt(curl, CURLOPT_TFTP_NO_OPTIONS, 1L);
2166
2167 /* new in 7.59.0 */
2168 if(config->happy_eyeballs_timeout_ms != CURL_HET_DEFAULT)
2169 my_setopt(curl, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS,
2170 config->happy_eyeballs_timeout_ms);
2171
2172 /* new in 7.60.0 */
2173 if(config->haproxy_protocol)
2174 my_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L);
2175
2176 /* new in 8.2.0 */
2177 if(config->haproxy_clientip)
2178 my_setopt_str(curl, CURLOPT_HAPROXY_CLIENT_IP,
2179 config->haproxy_clientip);
2180
2181 if(config->disallow_username_in_url)
2182 my_setopt(curl, CURLOPT_DISALLOW_USERNAME_IN_URL, 1L);
2183
2184 if(config->altsvc)
2185 my_setopt_str(curl, CURLOPT_ALTSVC, config->altsvc);
2186
2187 if(config->hsts)
2188 my_setopt_str(curl, CURLOPT_HSTS, config->hsts);
2189
2190 #ifdef USE_ECH
2191 /* only if enabled in configure */
2192 if(config->ech) /* only if set (optional) */
2193 my_setopt_str(curl, CURLOPT_ECH, config->ech);
2194 if(config->ech_public) /* only if set (optional) */
2195 my_setopt_str(curl, CURLOPT_ECH, config->ech_public);
2196 if(config->ech_config) /* only if set (optional) */
2197 my_setopt_str(curl, CURLOPT_ECH, config->ech_config);
2198 #endif
2199
2200 /* initialize retry vars for loop below */
2201 per->retry_sleep_default = (config->retry_delay) ?
2202 config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */
2203 per->retry_numretries = config->req_retry;
2204 per->retry_sleep = per->retry_sleep_default; /* ms */
2205 per->retrystart = tvnow();
2206
2207 state->li++;
2208 /* Here's looping around each globbed URL */
2209 if(state->li >= urlnum) {
2210 state->li = 0;
2211 state->urlnum = 0; /* forced reglob of URLs */
2212 glob_cleanup(state->urls);
2213 state->urls = NULL;
2214 state->up++;
2215 Curl_safefree(state->uploadfile); /* clear it to get the next */
2216 }
2217 }
2218 else {
2219 /* Free this URL node data without destroying the
2220 node itself nor modifying next pointer. */
2221 Curl_safefree(urlnode->outfile);
2222 Curl_safefree(urlnode->infile);
2223 urlnode->flags = 0;
2224 glob_cleanup(state->urls);
2225 state->urls = NULL;
2226 state->urlnum = 0;
2227
2228 Curl_safefree(state->outfiles);
2229 Curl_safefree(state->uploadfile);
2230 if(state->inglob) {
2231 /* Free list of globbed upload files */
2232 glob_cleanup(state->inglob);
2233 state->inglob = NULL;
2234 }
2235 config->state.urlnode = urlnode->next;
2236 state->up = 0;
2237 continue;
2238 }
2239 }
2240 break;
2241 }
2242 Curl_safefree(state->outfiles);
2243
2244 if(!*added || result) {
2245 *added = FALSE;
2246 single_transfer_cleanup(config);
2247 }
2248 return result;
2249 }
2250
2251 static long all_added; /* number of easy handles currently added */
2252
2253 /*
2254 * add_parallel_transfers() sets 'morep' to TRUE if there are more transfers
2255 * to add even after this call returns. sets 'addedp' to TRUE if one or more
2256 * transfers were added.
2257 */
add_parallel_transfers(struct GlobalConfig * global,CURLM * multi,CURLSH * share,bool * morep,bool * addedp)2258 static CURLcode add_parallel_transfers(struct GlobalConfig *global,
2259 CURLM *multi,
2260 CURLSH *share,
2261 bool *morep,
2262 bool *addedp)
2263 {
2264 struct per_transfer *per;
2265 CURLcode result = CURLE_OK;
2266 CURLMcode mcode;
2267 bool sleeping = FALSE;
2268 char *errorbuf;
2269 *addedp = FALSE;
2270 *morep = FALSE;
2271 if(all_pers < (global->parallel_max*2)) {
2272 result = create_transfer(global, share, addedp);
2273 if(result)
2274 return result;
2275 }
2276 for(per = transfers; per && (all_added < global->parallel_max);
2277 per = per->next) {
2278 bool getadded = FALSE;
2279 if(per->added)
2280 /* already added */
2281 continue;
2282 if(per->startat && (time(NULL) < per->startat)) {
2283 /* this is still delaying */
2284 sleeping = TRUE;
2285 continue;
2286 }
2287 per->added = TRUE;
2288
2289 result = pre_transfer(global, per);
2290 if(result)
2291 return result;
2292
2293 errorbuf = malloc(CURL_ERROR_SIZE);
2294 if(!errorbuf)
2295 return CURLE_OUT_OF_MEMORY;
2296
2297 /* parallel connect means that we don't set PIPEWAIT since pipewait
2298 will make libcurl prefer multiplexing */
2299 (void)curl_easy_setopt(per->curl, CURLOPT_PIPEWAIT,
2300 global->parallel_connect ? 0L : 1L);
2301 (void)curl_easy_setopt(per->curl, CURLOPT_PRIVATE, per);
2302 (void)curl_easy_setopt(per->curl, CURLOPT_XFERINFOFUNCTION, xferinfo_cb);
2303 (void)curl_easy_setopt(per->curl, CURLOPT_XFERINFODATA, per);
2304 (void)curl_easy_setopt(per->curl, CURLOPT_NOPROGRESS, 0L);
2305
2306 mcode = curl_multi_add_handle(multi, per->curl);
2307 if(mcode) {
2308 DEBUGASSERT(mcode == CURLM_OUT_OF_MEMORY);
2309 result = CURLE_OUT_OF_MEMORY;
2310 }
2311
2312 if(!result)
2313 result = create_transfer(global, share, &getadded);
2314 if(result) {
2315 free(errorbuf);
2316 return result;
2317 }
2318 errorbuf[0] = 0;
2319 (void)curl_easy_setopt(per->curl, CURLOPT_ERRORBUFFER, errorbuf);
2320 per->errorbuffer = errorbuf;
2321 per->added = TRUE;
2322 all_added++;
2323 *addedp = TRUE;
2324 }
2325 *morep = (per || sleeping) ? TRUE : FALSE;
2326 return CURLE_OK;
2327 }
2328
parallel_transfers(struct GlobalConfig * global,CURLSH * share)2329 static CURLcode parallel_transfers(struct GlobalConfig *global,
2330 CURLSH *share)
2331 {
2332 CURLM *multi;
2333 CURLMcode mcode = CURLM_OK;
2334 CURLcode result = CURLE_OK;
2335 int still_running = 1;
2336 struct timeval start = tvnow();
2337 bool more_transfers;
2338 bool added_transfers;
2339 /* wrapitup is set TRUE after a critical error occurs to end all transfers */
2340 bool wrapitup = FALSE;
2341 /* wrapitup_processed is set TRUE after the per transfer abort flag is set */
2342 bool wrapitup_processed = FALSE;
2343 time_t tick = time(NULL);
2344
2345 multi = curl_multi_init();
2346 if(!multi)
2347 return CURLE_OUT_OF_MEMORY;
2348
2349 result = add_parallel_transfers(global, multi, share,
2350 &more_transfers, &added_transfers);
2351 if(result) {
2352 curl_multi_cleanup(multi);
2353 return result;
2354 }
2355
2356 while(!mcode && (still_running || more_transfers)) {
2357 /* If stopping prematurely (eg due to a --fail-early condition) then signal
2358 that any transfers in the multi should abort (via progress callback). */
2359 if(wrapitup) {
2360 if(!still_running)
2361 break;
2362 if(!wrapitup_processed) {
2363 struct per_transfer *per;
2364 for(per = transfers; per; per = per->next) {
2365 if(per->added)
2366 per->abort = TRUE;
2367 }
2368 wrapitup_processed = TRUE;
2369 }
2370 }
2371
2372 mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL);
2373 if(!mcode)
2374 mcode = curl_multi_perform(multi, &still_running);
2375
2376 progress_meter(global, &start, FALSE);
2377
2378 if(!mcode) {
2379 int rc;
2380 CURLMsg *msg;
2381 bool checkmore = FALSE;
2382 do {
2383 msg = curl_multi_info_read(multi, &rc);
2384 if(msg) {
2385 bool retry;
2386 long delay;
2387 struct per_transfer *ended;
2388 CURL *easy = msg->easy_handle;
2389 CURLcode tres = msg->data.result;
2390 curl_easy_getinfo(easy, CURLINFO_PRIVATE, (void *)&ended);
2391 curl_multi_remove_handle(multi, easy);
2392
2393 if(ended->abort && (tres == CURLE_ABORTED_BY_CALLBACK) &&
2394 ended->errorbuffer) {
2395 msnprintf(ended->errorbuffer, CURL_ERROR_SIZE,
2396 "Transfer aborted due to critical error "
2397 "in another transfer");
2398 }
2399 tres = post_per_transfer(global, ended, tres, &retry, &delay);
2400 progress_finalize(ended); /* before it goes away */
2401 all_added--; /* one fewer added */
2402 checkmore = TRUE;
2403 if(retry) {
2404 ended->added = FALSE; /* add it again */
2405 /* we delay retries in full integer seconds only */
2406 ended->startat = delay ? time(NULL) + delay/1000 : 0;
2407 }
2408 else {
2409 /* result receives this transfer's error unless the transfer was
2410 marked for abort due to a critical error in another transfer */
2411 if(tres && (!ended->abort || !result))
2412 result = tres;
2413 if(is_fatal_error(result) || (result && global->fail_early))
2414 wrapitup = TRUE;
2415 (void)del_per_transfer(ended);
2416 }
2417 }
2418 } while(msg);
2419 if(wrapitup) {
2420 if(still_running)
2421 continue;
2422 else
2423 break;
2424 }
2425 if(!checkmore) {
2426 time_t tock = time(NULL);
2427 if(tick != tock) {
2428 checkmore = TRUE;
2429 tick = tock;
2430 }
2431 }
2432 if(checkmore) {
2433 /* one or more transfers completed, add more! */
2434 CURLcode tres = add_parallel_transfers(global, multi, share,
2435 &more_transfers,
2436 &added_transfers);
2437 if(tres)
2438 result = tres;
2439 if(added_transfers)
2440 /* we added new ones, make sure the loop doesn't exit yet */
2441 still_running = 1;
2442 }
2443 if(is_fatal_error(result) || (result && global->fail_early))
2444 wrapitup = TRUE;
2445 }
2446 }
2447
2448 (void)progress_meter(global, &start, TRUE);
2449
2450 /* Make sure to return some kind of error if there was a multi problem */
2451 if(mcode) {
2452 result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY :
2453 /* The other multi errors should never happen, so return
2454 something suitably generic */
2455 CURLE_BAD_FUNCTION_ARGUMENT;
2456 }
2457
2458 curl_multi_cleanup(multi);
2459
2460 return result;
2461 }
2462
serial_transfers(struct GlobalConfig * global,CURLSH * share)2463 static CURLcode serial_transfers(struct GlobalConfig *global,
2464 CURLSH *share)
2465 {
2466 CURLcode returncode = CURLE_OK;
2467 CURLcode result = CURLE_OK;
2468 struct per_transfer *per;
2469 bool added = FALSE;
2470
2471 result = create_transfer(global, share, &added);
2472 if(result)
2473 return result;
2474 if(!added) {
2475 errorf(global, "no transfer performed");
2476 return CURLE_READ_ERROR;
2477 }
2478 for(per = transfers; per;) {
2479 bool retry;
2480 long delay_ms;
2481 bool bailout = FALSE;
2482 struct timeval start;
2483 result = pre_transfer(global, per);
2484 if(result)
2485 break;
2486
2487 if(global->libcurl) {
2488 result = easysrc_perform();
2489 if(result)
2490 break;
2491 }
2492 start = tvnow();
2493 #ifdef CURLDEBUG
2494 if(global->test_event_based)
2495 result = curl_easy_perform_ev(per->curl);
2496 else
2497 #endif
2498 result = curl_easy_perform(per->curl);
2499
2500 returncode = post_per_transfer(global, per, result, &retry, &delay_ms);
2501 if(retry) {
2502 tool_go_sleep(delay_ms);
2503 continue;
2504 }
2505
2506 /* Bail out upon critical errors or --fail-early */
2507 if(is_fatal_error(returncode) || (returncode && global->fail_early))
2508 bailout = TRUE;
2509 else {
2510 /* setup the next one just before we delete this */
2511 result = create_transfer(global, share, &added);
2512 if(result) {
2513 returncode = result;
2514 bailout = TRUE;
2515 }
2516 }
2517
2518 per = del_per_transfer(per);
2519
2520 if(bailout)
2521 break;
2522
2523 if(per && global->ms_per_transfer) {
2524 /* how long time did the most recent transfer take in number of
2525 milliseconds */
2526 long milli = tvdiff(tvnow(), start);
2527 if(milli < global->ms_per_transfer) {
2528 notef(global, "Transfer took %ld ms, waits %ldms as set by --rate",
2529 milli, global->ms_per_transfer - milli);
2530 /* The transfer took less time than wanted. Wait a little. */
2531 tool_go_sleep(global->ms_per_transfer - milli);
2532 }
2533 }
2534 }
2535 if(returncode)
2536 /* returncode errors have priority */
2537 result = returncode;
2538
2539 if(result)
2540 single_transfer_cleanup(global->current);
2541
2542 return result;
2543 }
2544
2545 /* setup a transfer for the given config */
transfer_per_config(struct GlobalConfig * global,struct OperationConfig * config,CURLSH * share,bool * added)2546 static CURLcode transfer_per_config(struct GlobalConfig *global,
2547 struct OperationConfig *config,
2548 CURLSH *share,
2549 bool *added)
2550 {
2551 CURLcode result = CURLE_OK;
2552 bool capath_from_env;
2553 *added = FALSE;
2554
2555 /* Check we have a url */
2556 if(!config->url_list || !config->url_list->url) {
2557 helpf(tool_stderr, "(%d) no URL specified", CURLE_FAILED_INIT);
2558 return CURLE_FAILED_INIT;
2559 }
2560
2561 /* On WIN32 we can't set the path to curl-ca-bundle.crt
2562 * at compile time. So we look here for the file in two ways:
2563 * 1: look at the environment variable CURL_CA_BUNDLE for a path
2564 * 2: if #1 isn't found, use the windows API function SearchPath()
2565 * to find it along the app's path (includes app's dir and CWD)
2566 *
2567 * We support the environment variable thing for non-Windows platforms
2568 * too. Just for the sake of it.
2569 */
2570 capath_from_env = false;
2571 if(!config->cacert &&
2572 !config->capath &&
2573 (!config->insecure_ok || (config->doh_url && !config->doh_insecure_ok))) {
2574 CURL *curltls = curl_easy_init();
2575 struct curl_tlssessioninfo *tls_backend_info = NULL;
2576
2577 /* With the addition of CAINFO support for Schannel, this search could find
2578 * a certificate bundle that was previously ignored. To maintain backward
2579 * compatibility, only perform this search if not using Schannel.
2580 */
2581 result = curl_easy_getinfo(curltls, CURLINFO_TLS_SSL_PTR,
2582 &tls_backend_info);
2583 if(result) {
2584 curl_easy_cleanup(curltls);
2585 return result;
2586 }
2587
2588 /* Set the CA cert locations specified in the environment. For Windows if
2589 * no environment-specified filename is found then check for CA bundle
2590 * default filename curl-ca-bundle.crt in the user's PATH.
2591 *
2592 * If Schannel is the selected SSL backend then these locations are
2593 * ignored. We allow setting CA location for schannel only when explicitly
2594 * specified by the user via CURLOPT_CAINFO / --cacert.
2595 */
2596 if(tls_backend_info->backend != CURLSSLBACKEND_SCHANNEL) {
2597 char *env;
2598 env = curl_getenv("CURL_CA_BUNDLE");
2599 if(env) {
2600 config->cacert = strdup(env);
2601 curl_free(env);
2602 if(!config->cacert) {
2603 curl_easy_cleanup(curltls);
2604 errorf(global, "out of memory");
2605 return CURLE_OUT_OF_MEMORY;
2606 }
2607 }
2608 else {
2609 env = curl_getenv("SSL_CERT_DIR");
2610 if(env) {
2611 config->capath = strdup(env);
2612 curl_free(env);
2613 if(!config->capath) {
2614 curl_easy_cleanup(curltls);
2615 errorf(global, "out of memory");
2616 return CURLE_OUT_OF_MEMORY;
2617 }
2618 capath_from_env = true;
2619 }
2620 env = curl_getenv("SSL_CERT_FILE");
2621 if(env) {
2622 config->cacert = strdup(env);
2623 curl_free(env);
2624 if(!config->cacert) {
2625 if(capath_from_env)
2626 free(config->capath);
2627 curl_easy_cleanup(curltls);
2628 errorf(global, "out of memory");
2629 return CURLE_OUT_OF_MEMORY;
2630 }
2631 }
2632 }
2633
2634 #ifdef _WIN32
2635 if(!env)
2636 result = FindWin32CACert(config, tls_backend_info->backend,
2637 TEXT("curl-ca-bundle.crt"));
2638 #endif
2639 }
2640 curl_easy_cleanup(curltls);
2641 }
2642
2643 if(!result)
2644 result = single_transfer(global, config, share, capath_from_env, added);
2645
2646 return result;
2647 }
2648
2649 /*
2650 * 'create_transfer' gets the details and sets up a new transfer if 'added'
2651 * returns TRUE.
2652 */
create_transfer(struct GlobalConfig * global,CURLSH * share,bool * added)2653 static CURLcode create_transfer(struct GlobalConfig *global,
2654 CURLSH *share,
2655 bool *added)
2656 {
2657 CURLcode result = CURLE_OK;
2658 *added = FALSE;
2659 while(global->current) {
2660 result = transfer_per_config(global, global->current, share, added);
2661 if(!result && !*added) {
2662 /* when one set is drained, continue to next */
2663 global->current = global->current->next;
2664 continue;
2665 }
2666 break;
2667 }
2668 return result;
2669 }
2670
run_all_transfers(struct GlobalConfig * global,CURLSH * share,CURLcode result)2671 static CURLcode run_all_transfers(struct GlobalConfig *global,
2672 CURLSH *share,
2673 CURLcode result)
2674 {
2675 /* Save the values of noprogress and isatty to restore them later on */
2676 bool orig_noprogress = global->noprogress;
2677 bool orig_isatty = global->isatty;
2678 struct per_transfer *per;
2679
2680 /* Time to actually do the transfers */
2681 if(!result) {
2682 if(global->parallel)
2683 result = parallel_transfers(global, share);
2684 else
2685 result = serial_transfers(global, share);
2686 }
2687
2688 /* cleanup if there are any left */
2689 for(per = transfers; per;) {
2690 bool retry;
2691 long delay;
2692 CURLcode result2 = post_per_transfer(global, per, result, &retry, &delay);
2693 if(!result)
2694 /* don't overwrite the original error */
2695 result = result2;
2696
2697 /* Free list of given URLs */
2698 clean_getout(per->config);
2699
2700 per = del_per_transfer(per);
2701 }
2702
2703 /* Reset the global config variables */
2704 global->noprogress = orig_noprogress;
2705 global->isatty = orig_isatty;
2706
2707
2708 return result;
2709 }
2710
operate(struct GlobalConfig * global,int argc,argv_item_t argv[])2711 CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[])
2712 {
2713 CURLcode result = CURLE_OK;
2714 char *first_arg = argc > 1 ? curlx_convert_tchar_to_UTF8(argv[1]) : NULL;
2715
2716 #ifdef HAVE_SETLOCALE
2717 /* Override locale for number parsing (only) */
2718 setlocale(LC_ALL, "");
2719 setlocale(LC_NUMERIC, "C");
2720 #endif
2721
2722 /* Parse .curlrc if necessary */
2723 if((argc == 1) ||
2724 (first_arg && strncmp(first_arg, "-q", 2) &&
2725 !curl_strequal(first_arg, "--disable"))) {
2726 parseconfig(NULL, global); /* ignore possible failure */
2727
2728 /* If we had no arguments then make sure a url was specified in .curlrc */
2729 if((argc < 2) && (!global->first->url_list)) {
2730 helpf(tool_stderr, NULL);
2731 result = CURLE_FAILED_INIT;
2732 }
2733 }
2734
2735 curlx_unicodefree(first_arg);
2736
2737 if(!result) {
2738 /* Parse the command line arguments */
2739 ParameterError res = parse_args(global, argc, argv);
2740 if(res) {
2741 result = CURLE_OK;
2742
2743 /* Check if we were asked for the help */
2744 if(res == PARAM_HELP_REQUESTED)
2745 tool_help(global->help_category);
2746 /* Check if we were asked for the manual */
2747 else if(res == PARAM_MANUAL_REQUESTED)
2748 hugehelp();
2749 /* Check if we were asked for the version information */
2750 else if(res == PARAM_VERSION_INFO_REQUESTED)
2751 tool_version_info();
2752 /* Check if we were asked to list the SSL engines */
2753 else if(res == PARAM_ENGINES_REQUESTED)
2754 tool_list_engines();
2755 else if(res == PARAM_LIBCURL_UNSUPPORTED_PROTOCOL)
2756 result = CURLE_UNSUPPORTED_PROTOCOL;
2757 else if(res == PARAM_READ_ERROR)
2758 result = CURLE_READ_ERROR;
2759 else
2760 result = CURLE_FAILED_INIT;
2761 }
2762 else {
2763 if(global->libcurl) {
2764 /* Initialise the libcurl source output */
2765 result = easysrc_init();
2766 }
2767
2768 /* Perform the main operations */
2769 if(!result) {
2770 size_t count = 0;
2771 struct OperationConfig *operation = global->first;
2772 CURLSH *share = curl_share_init();
2773 if(!share) {
2774 if(global->libcurl) {
2775 /* Cleanup the libcurl source output */
2776 easysrc_cleanup();
2777 }
2778 result = CURLE_OUT_OF_MEMORY;
2779 }
2780
2781 if(!result) {
2782 curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
2783 curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
2784 curl_share_setopt(share, CURLSHOPT_SHARE,
2785 CURL_LOCK_DATA_SSL_SESSION);
2786 curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
2787 curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
2788 curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
2789
2790 /* Get the required arguments for each operation */
2791 do {
2792 result = get_args(operation, count++);
2793
2794 operation = operation->next;
2795 } while(!result && operation);
2796
2797 /* Set the current operation pointer */
2798 global->current = global->first;
2799
2800 /* now run! */
2801 result = run_all_transfers(global, share, result);
2802
2803 curl_share_cleanup(share);
2804 if(global->libcurl) {
2805 /* Cleanup the libcurl source output */
2806 easysrc_cleanup();
2807
2808 /* Dump the libcurl code if previously enabled */
2809 dumpeasysrc(global);
2810 }
2811 }
2812 }
2813 else
2814 errorf(global, "out of memory");
2815 }
2816 }
2817
2818 varcleanup(global);
2819
2820 return result;
2821 }
2822