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