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