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