1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel (at) haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22 23 #include "curl_setup.h" 24 25 #ifndef CURL_DISABLE_FILE 26 27 #ifdef HAVE_NETINET_IN_H 28 #include <netinet/in.h> 29 #endif 30 #ifdef HAVE_NETDB_H 31 #include <netdb.h> 32 #endif 33 #ifdef HAVE_ARPA_INET_H 34 #include <arpa/inet.h> 35 #endif 36 #ifdef HAVE_NET_IF_H 37 #include <net/if.h> 38 #endif 39 #ifdef HAVE_SYS_IOCTL_H 40 #include <sys/ioctl.h> 41 #endif 42 43 #ifdef HAVE_SYS_PARAM_H 44 #include <sys/param.h> 45 #endif 46 47 #ifdef HAVE_FCNTL_H 48 #include <fcntl.h> 49 #endif 50 51 #include "strtoofft.h" 52 #include "urldata.h" 53 #include <curl/curl.h> 54 #include "progress.h" 55 #include "sendf.h" 56 #include "escape.h" 57 #include "file.h" 58 #include "speedcheck.h" 59 #include "getinfo.h" 60 #include "transfer.h" 61 #include "url.h" 62 #include "parsedate.h" /* for the week day and month names */ 63 #include "warnless.h" 64 /* The last 3 #include files should be in this order */ 65 #include "curl_printf.h" 66 #include "curl_memory.h" 67 #include "memdebug.h" 68 69 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \ 70 defined(__SYMBIAN32__) 71 #define DOS_FILESYSTEM 1 72 #endif 73 74 #ifdef OPEN_NEEDS_ARG3 75 # define open_readonly(p,f) open((p),(f),(0)) 76 #else 77 # define open_readonly(p,f) open((p),(f)) 78 #endif 79 80 /* 81 * Forward declarations. 82 */ 83 84 static CURLcode file_do(struct connectdata *, bool *done); 85 static CURLcode file_done(struct connectdata *conn, 86 CURLcode status, bool premature); 87 static CURLcode file_connect(struct connectdata *conn, bool *done); 88 static CURLcode file_disconnect(struct connectdata *conn, 89 bool dead_connection); 90 static CURLcode file_setup_connection(struct connectdata *conn); 91 92 /* 93 * FILE scheme handler. 94 */ 95 96 const struct Curl_handler Curl_handler_file = { 97 "FILE", /* scheme */ 98 file_setup_connection, /* setup_connection */ 99 file_do, /* do_it */ 100 file_done, /* done */ 101 ZERO_NULL, /* do_more */ 102 file_connect, /* connect_it */ 103 ZERO_NULL, /* connecting */ 104 ZERO_NULL, /* doing */ 105 ZERO_NULL, /* proto_getsock */ 106 ZERO_NULL, /* doing_getsock */ 107 ZERO_NULL, /* domore_getsock */ 108 ZERO_NULL, /* perform_getsock */ 109 file_disconnect, /* disconnect */ 110 ZERO_NULL, /* readwrite */ 111 ZERO_NULL, /* connection_check */ 112 0, /* defport */ 113 CURLPROTO_FILE, /* protocol */ 114 PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */ 115 }; 116 117 118 static CURLcode file_setup_connection(struct connectdata *conn) 119 { 120 /* allocate the FILE specific struct */ 121 conn->data->req.protop = calloc(1, sizeof(struct FILEPROTO)); 122 if(!conn->data->req.protop) 123 return CURLE_OUT_OF_MEMORY; 124 125 return CURLE_OK; 126 } 127 128 /* 129 Check if this is a range download, and if so, set the internal variables 130 properly. This code is copied from the FTP implementation and might as 131 well be factored out. 132 */ 133 static CURLcode file_range(struct connectdata *conn) 134 { 135 curl_off_t from, to; 136 curl_off_t totalsize = -1; 137 char *ptr; 138 char *ptr2; 139 struct Curl_easy *data = conn->data; 140 141 if(data->state.use_range && data->state.range) { 142 CURLofft from_t; 143 CURLofft to_t; 144 from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from); 145 if(from_t == CURL_OFFT_FLOW) 146 return CURLE_RANGE_ERROR; 147 while(*ptr && (ISSPACE(*ptr) || (*ptr == '-'))) 148 ptr++; 149 to_t = curlx_strtoofft(ptr, &ptr2, 0, &to); 150 if(to_t == CURL_OFFT_FLOW) 151 return CURLE_RANGE_ERROR; 152 if((to_t == CURL_OFFT_INVAL) && !from_t) { 153 /* X - */ 154 data->state.resume_from = from; 155 DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file\n", 156 from)); 157 } 158 else if((from_t == CURL_OFFT_INVAL) && !to_t) { 159 /* -Y */ 160 data->req.maxdownload = to; 161 data->state.resume_from = -to; 162 DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes\n", 163 to)); 164 } 165 else { 166 /* X-Y */ 167 totalsize = to-from; 168 if(totalsize == CURL_OFF_T_MAX) 169 /* this is too big to increase, so bail out */ 170 return CURLE_RANGE_ERROR; 171 data->req.maxdownload = totalsize + 1; /* include last byte */ 172 data->state.resume_from = from; 173 DEBUGF(infof(data, "RANGE from %" CURL_FORMAT_CURL_OFF_T 174 " getting %" CURL_FORMAT_CURL_OFF_T " bytes\n", 175 from, data->req.maxdownload)); 176 } 177 DEBUGF(infof(data, "range-download from %" CURL_FORMAT_CURL_OFF_T 178 " to %" CURL_FORMAT_CURL_OFF_T ", totally %" 179 CURL_FORMAT_CURL_OFF_T " bytes\n", 180 from, to, data->req.maxdownload)); 181 } 182 else 183 data->req.maxdownload = -1; 184 return CURLE_OK; 185 } 186 187 /* 188 * file_connect() gets called from Curl_protocol_connect() to allow us to 189 * do protocol-specific actions at connect-time. We emulate a 190 * connect-then-transfer protocol and "connect" to the file here 191 */ 192 static CURLcode file_connect(struct connectdata *conn, bool *done) 193 { 194 struct Curl_easy *data = conn->data; 195 char *real_path; 196 struct FILEPROTO *file = data->req.protop; 197 int fd; 198 #ifdef DOS_FILESYSTEM 199 size_t i; 200 char *actual_path; 201 #endif 202 size_t real_path_len; 203 204 CURLcode result = Curl_urldecode(data, data->state.path, 0, &real_path, 205 &real_path_len, FALSE); 206 if(result) 207 return result; 208 209 #ifdef DOS_FILESYSTEM 210 /* If the first character is a slash, and there's 211 something that looks like a drive at the beginning of 212 the path, skip the slash. If we remove the initial 213 slash in all cases, paths without drive letters end up 214 relative to the current directory which isn't how 215 browsers work. 216 217 Some browsers accept | instead of : as the drive letter 218 separator, so we do too. 219 220 On other platforms, we need the slash to indicate an 221 absolute pathname. On Windows, absolute paths start 222 with a drive letter. 223 */ 224 actual_path = real_path; 225 if((actual_path[0] == '/') && 226 actual_path[1] && 227 (actual_path[2] == ':' || actual_path[2] == '|')) { 228 actual_path[2] = ':'; 229 actual_path++; 230 real_path_len--; 231 } 232 233 /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */ 234 for(i = 0; i < real_path_len; ++i) 235 if(actual_path[i] == '/') 236 actual_path[i] = '\\'; 237 else if(!actual_path[i]) { /* binary zero */ 238 Curl_safefree(real_path); 239 return CURLE_URL_MALFORMAT; 240 } 241 242 fd = open_readonly(actual_path, O_RDONLY|O_BINARY); 243 file->path = actual_path; 244 #else 245 if(memchr(real_path, 0, real_path_len)) { 246 /* binary zeroes indicate foul play */ 247 Curl_safefree(real_path); 248 return CURLE_URL_MALFORMAT; 249 } 250 251 fd = open_readonly(real_path, O_RDONLY); 252 file->path = real_path; 253 #endif 254 file->freepath = real_path; /* free this when done */ 255 256 file->fd = fd; 257 if(!data->set.upload && (fd == -1)) { 258 failf(data, "Couldn't open file %s", data->state.path); 259 file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE); 260 return CURLE_FILE_COULDNT_READ_FILE; 261 } 262 *done = TRUE; 263 264 return CURLE_OK; 265 } 266 267 static CURLcode file_done(struct connectdata *conn, 268 CURLcode status, bool premature) 269 { 270 struct FILEPROTO *file = conn->data->req.protop; 271 (void)status; /* not used */ 272 (void)premature; /* not used */ 273 274 if(file) { 275 Curl_safefree(file->freepath); 276 file->path = NULL; 277 if(file->fd != -1) 278 close(file->fd); 279 file->fd = -1; 280 } 281 282 return CURLE_OK; 283 } 284 285 static CURLcode file_disconnect(struct connectdata *conn, 286 bool dead_connection) 287 { 288 struct FILEPROTO *file = conn->data->req.protop; 289 (void)dead_connection; /* not used */ 290 291 if(file) { 292 Curl_safefree(file->freepath); 293 file->path = NULL; 294 if(file->fd != -1) 295 close(file->fd); 296 file->fd = -1; 297 } 298 299 return CURLE_OK; 300 } 301 302 #ifdef DOS_FILESYSTEM 303 #define DIRSEP '\\' 304 #else 305 #define DIRSEP '/' 306 #endif 307 308 static CURLcode file_upload(struct connectdata *conn) 309 { 310 struct FILEPROTO *file = conn->data->req.protop; 311 const char *dir = strchr(file->path, DIRSEP); 312 int fd; 313 int mode; 314 CURLcode result = CURLE_OK; 315 struct Curl_easy *data = conn->data; 316 char *buf = data->state.buffer; 317 size_t nread; 318 size_t nwrite; 319 curl_off_t bytecount = 0; 320 struct_stat file_stat; 321 const char *buf2; 322 323 /* 324 * Since FILE: doesn't do the full init, we need to provide some extra 325 * assignments here. 326 */ 327 conn->data->req.upload_fromhere = buf; 328 329 if(!dir) 330 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */ 331 332 if(!dir[1]) 333 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */ 334 335 #ifdef O_BINARY 336 #define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY 337 #else 338 #define MODE_DEFAULT O_WRONLY|O_CREAT 339 #endif 340 341 if(data->state.resume_from) 342 mode = MODE_DEFAULT|O_APPEND; 343 else 344 mode = MODE_DEFAULT|O_TRUNC; 345 346 fd = open(file->path, mode, conn->data->set.new_file_perms); 347 if(fd < 0) { 348 failf(data, "Can't open %s for writing", file->path); 349 return CURLE_WRITE_ERROR; 350 } 351 352 if(-1 != data->state.infilesize) 353 /* known size of data to "upload" */ 354 Curl_pgrsSetUploadSize(data, data->state.infilesize); 355 356 /* treat the negative resume offset value as the case of "-" */ 357 if(data->state.resume_from < 0) { 358 if(fstat(fd, &file_stat)) { 359 close(fd); 360 failf(data, "Can't get the size of %s", file->path); 361 return CURLE_WRITE_ERROR; 362 } 363 data->state.resume_from = (curl_off_t)file_stat.st_size; 364 } 365 366 while(!result) { 367 int readcount; 368 result = Curl_fillreadbuffer(conn, (int)data->set.buffer_size, &readcount); 369 if(result) 370 break; 371 372 if(readcount <= 0) /* fix questionable compare error. curlvms */ 373 break; 374 375 nread = (size_t)readcount; 376 377 /*skip bytes before resume point*/ 378 if(data->state.resume_from) { 379 if((curl_off_t)nread <= data->state.resume_from) { 380 data->state.resume_from -= nread; 381 nread = 0; 382 buf2 = buf; 383 } 384 else { 385 buf2 = buf + data->state.resume_from; 386 nread -= (size_t)data->state.resume_from; 387 data->state.resume_from = 0; 388 } 389 } 390 else 391 buf2 = buf; 392 393 /* write the data to the target */ 394 nwrite = write(fd, buf2, nread); 395 if(nwrite != nread) { 396 result = CURLE_SEND_ERROR; 397 break; 398 } 399 400 bytecount += nread; 401 402 Curl_pgrsSetUploadCounter(data, bytecount); 403 404 if(Curl_pgrsUpdate(conn)) 405 result = CURLE_ABORTED_BY_CALLBACK; 406 else 407 result = Curl_speedcheck(data, Curl_now()); 408 } 409 if(!result && Curl_pgrsUpdate(conn)) 410 result = CURLE_ABORTED_BY_CALLBACK; 411 412 close(fd); 413 414 return result; 415 } 416 417 /* 418 * file_do() is the protocol-specific function for the do-phase, separated 419 * from the connect-phase above. Other protocols merely setup the transfer in 420 * the do-phase, to have it done in the main transfer loop but since some 421 * platforms we support don't allow select()ing etc on file handles (as 422 * opposed to sockets) we instead perform the whole do-operation in this 423 * function. 424 */ 425 static CURLcode file_do(struct connectdata *conn, bool *done) 426 { 427 /* This implementation ignores the host name in conformance with 428 RFC 1738. Only local files (reachable via the standard file system) 429 are supported. This means that files on remotely mounted directories 430 (via NFS, Samba, NT sharing) can be accessed through a file:// URL 431 */ 432 CURLcode result = CURLE_OK; 433 struct_stat statbuf; /* struct_stat instead of struct stat just to allow the 434 Windows version to have a different struct without 435 having to redefine the simple word 'stat' */ 436 curl_off_t expected_size = 0; 437 bool size_known; 438 bool fstated = FALSE; 439 ssize_t nread; 440 struct Curl_easy *data = conn->data; 441 char *buf = data->state.buffer; 442 curl_off_t bytecount = 0; 443 int fd; 444 struct FILEPROTO *file; 445 446 *done = TRUE; /* unconditionally */ 447 448 Curl_initinfo(data); 449 Curl_pgrsStartNow(data); 450 451 if(data->set.upload) 452 return file_upload(conn); 453 454 file = conn->data->req.protop; 455 456 /* get the fd from the connection phase */ 457 fd = file->fd; 458 459 /* VMS: This only works reliable for STREAMLF files */ 460 if(-1 != fstat(fd, &statbuf)) { 461 /* we could stat it, then read out the size */ 462 expected_size = statbuf.st_size; 463 /* and store the modification time */ 464 data->info.filetime = (long)statbuf.st_mtime; 465 fstated = TRUE; 466 } 467 468 if(fstated && !data->state.range && data->set.timecondition) { 469 if(!Curl_meets_timecondition(data, (time_t)data->info.filetime)) { 470 *done = TRUE; 471 return CURLE_OK; 472 } 473 } 474 475 /* If we have selected NOBODY and HEADER, it means that we only want file 476 information. Which for FILE can't be much more than the file size and 477 date. */ 478 if(data->set.opt_no_body && data->set.include_header && fstated) { 479 time_t filetime; 480 struct tm buffer; 481 const struct tm *tm = &buffer; 482 char header[80]; 483 snprintf(header, sizeof(header), 484 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size); 485 result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0); 486 if(result) 487 return result; 488 489 result = Curl_client_write(conn, CLIENTWRITE_BOTH, 490 (char *)"Accept-ranges: bytes\r\n", 0); 491 if(result) 492 return result; 493 494 filetime = (time_t)statbuf.st_mtime; 495 result = Curl_gmtime(filetime, &buffer); 496 if(result) 497 return result; 498 499 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ 500 snprintf(header, sizeof(header), 501 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", 502 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], 503 tm->tm_mday, 504 Curl_month[tm->tm_mon], 505 tm->tm_year + 1900, 506 tm->tm_hour, 507 tm->tm_min, 508 tm->tm_sec); 509 result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0); 510 if(!result) 511 /* set the file size to make it available post transfer */ 512 Curl_pgrsSetDownloadSize(data, expected_size); 513 return result; 514 } 515 516 /* Check whether file range has been specified */ 517 file_range(conn); 518 519 /* Adjust the start offset in case we want to get the N last bytes 520 * of the stream iff the filesize could be determined */ 521 if(data->state.resume_from < 0) { 522 if(!fstated) { 523 failf(data, "Can't get the size of file."); 524 return CURLE_READ_ERROR; 525 } 526 data->state.resume_from += (curl_off_t)statbuf.st_size; 527 } 528 529 if(data->state.resume_from <= expected_size) 530 expected_size -= data->state.resume_from; 531 else { 532 failf(data, "failed to resume file:// transfer"); 533 return CURLE_BAD_DOWNLOAD_RESUME; 534 } 535 536 /* A high water mark has been specified so we obey... */ 537 if(data->req.maxdownload > 0) 538 expected_size = data->req.maxdownload; 539 540 if(!fstated || (expected_size == 0)) 541 size_known = FALSE; 542 else 543 size_known = TRUE; 544 545 /* The following is a shortcut implementation of file reading 546 this is both more efficient than the former call to download() and 547 it avoids problems with select() and recv() on file descriptors 548 in Winsock */ 549 if(fstated) 550 Curl_pgrsSetDownloadSize(data, expected_size); 551 552 if(data->state.resume_from) { 553 if(data->state.resume_from != 554 lseek(fd, data->state.resume_from, SEEK_SET)) 555 return CURLE_BAD_DOWNLOAD_RESUME; 556 } 557 558 Curl_pgrsTime(data, TIMER_STARTTRANSFER); 559 560 while(!result) { 561 /* Don't fill a whole buffer if we want less than all data */ 562 size_t bytestoread; 563 564 if(size_known) { 565 bytestoread = (expected_size < data->set.buffer_size) ? 566 curlx_sotouz(expected_size) : (size_t)data->set.buffer_size; 567 } 568 else 569 bytestoread = data->set.buffer_size-1; 570 571 nread = read(fd, buf, bytestoread); 572 573 if(nread > 0) 574 buf[nread] = 0; 575 576 if(nread <= 0 || (size_known && (expected_size == 0))) 577 break; 578 579 bytecount += nread; 580 if(size_known) 581 expected_size -= nread; 582 583 result = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread); 584 if(result) 585 return result; 586 587 Curl_pgrsSetDownloadCounter(data, bytecount); 588 589 if(Curl_pgrsUpdate(conn)) 590 result = CURLE_ABORTED_BY_CALLBACK; 591 else 592 result = Curl_speedcheck(data, Curl_now()); 593 } 594 if(Curl_pgrsUpdate(conn)) 595 result = CURLE_ABORTED_BY_CALLBACK; 596 597 return result; 598 } 599 600 #endif 601