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