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