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