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