113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#ifndef CURL_DISABLE_FILE 2813498266Sopenharmony_ci 2913498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H 3013498266Sopenharmony_ci#include <netinet/in.h> 3113498266Sopenharmony_ci#endif 3213498266Sopenharmony_ci#ifdef HAVE_NETDB_H 3313498266Sopenharmony_ci#include <netdb.h> 3413498266Sopenharmony_ci#endif 3513498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H 3613498266Sopenharmony_ci#include <arpa/inet.h> 3713498266Sopenharmony_ci#endif 3813498266Sopenharmony_ci#ifdef HAVE_NET_IF_H 3913498266Sopenharmony_ci#include <net/if.h> 4013498266Sopenharmony_ci#endif 4113498266Sopenharmony_ci#ifdef HAVE_SYS_IOCTL_H 4213498266Sopenharmony_ci#include <sys/ioctl.h> 4313498266Sopenharmony_ci#endif 4413498266Sopenharmony_ci 4513498266Sopenharmony_ci#ifdef HAVE_SYS_PARAM_H 4613498266Sopenharmony_ci#include <sys/param.h> 4713498266Sopenharmony_ci#endif 4813498266Sopenharmony_ci 4913498266Sopenharmony_ci#ifdef HAVE_FCNTL_H 5013498266Sopenharmony_ci#include <fcntl.h> 5113498266Sopenharmony_ci#endif 5213498266Sopenharmony_ci 5313498266Sopenharmony_ci#include "strtoofft.h" 5413498266Sopenharmony_ci#include "urldata.h" 5513498266Sopenharmony_ci#include <curl/curl.h> 5613498266Sopenharmony_ci#include "progress.h" 5713498266Sopenharmony_ci#include "sendf.h" 5813498266Sopenharmony_ci#include "escape.h" 5913498266Sopenharmony_ci#include "file.h" 6013498266Sopenharmony_ci#include "speedcheck.h" 6113498266Sopenharmony_ci#include "getinfo.h" 6213498266Sopenharmony_ci#include "transfer.h" 6313498266Sopenharmony_ci#include "url.h" 6413498266Sopenharmony_ci#include "parsedate.h" /* for the week day and month names */ 6513498266Sopenharmony_ci#include "warnless.h" 6613498266Sopenharmony_ci#include "curl_range.h" 6713498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 6813498266Sopenharmony_ci#include "curl_printf.h" 6913498266Sopenharmony_ci#include "curl_memory.h" 7013498266Sopenharmony_ci#include "memdebug.h" 7113498266Sopenharmony_ci 7213498266Sopenharmony_ci#if defined(_WIN32) || defined(MSDOS) || defined(__EMX__) 7313498266Sopenharmony_ci#define DOS_FILESYSTEM 1 7413498266Sopenharmony_ci#elif defined(__amigaos4__) 7513498266Sopenharmony_ci#define AMIGA_FILESYSTEM 1 7613498266Sopenharmony_ci#endif 7713498266Sopenharmony_ci 7813498266Sopenharmony_ci#ifdef OPEN_NEEDS_ARG3 7913498266Sopenharmony_ci# define open_readonly(p,f) open((p),(f),(0)) 8013498266Sopenharmony_ci#else 8113498266Sopenharmony_ci# define open_readonly(p,f) open((p),(f)) 8213498266Sopenharmony_ci#endif 8313498266Sopenharmony_ci 8413498266Sopenharmony_ci/* 8513498266Sopenharmony_ci * Forward declarations. 8613498266Sopenharmony_ci */ 8713498266Sopenharmony_ci 8813498266Sopenharmony_cistatic CURLcode file_do(struct Curl_easy *data, bool *done); 8913498266Sopenharmony_cistatic CURLcode file_done(struct Curl_easy *data, 9013498266Sopenharmony_ci CURLcode status, bool premature); 9113498266Sopenharmony_cistatic CURLcode file_connect(struct Curl_easy *data, bool *done); 9213498266Sopenharmony_cistatic CURLcode file_disconnect(struct Curl_easy *data, 9313498266Sopenharmony_ci struct connectdata *conn, 9413498266Sopenharmony_ci bool dead_connection); 9513498266Sopenharmony_cistatic CURLcode file_setup_connection(struct Curl_easy *data, 9613498266Sopenharmony_ci struct connectdata *conn); 9713498266Sopenharmony_ci 9813498266Sopenharmony_ci/* 9913498266Sopenharmony_ci * FILE scheme handler. 10013498266Sopenharmony_ci */ 10113498266Sopenharmony_ci 10213498266Sopenharmony_ciconst struct Curl_handler Curl_handler_file = { 10313498266Sopenharmony_ci "FILE", /* scheme */ 10413498266Sopenharmony_ci file_setup_connection, /* setup_connection */ 10513498266Sopenharmony_ci file_do, /* do_it */ 10613498266Sopenharmony_ci file_done, /* done */ 10713498266Sopenharmony_ci ZERO_NULL, /* do_more */ 10813498266Sopenharmony_ci file_connect, /* connect_it */ 10913498266Sopenharmony_ci ZERO_NULL, /* connecting */ 11013498266Sopenharmony_ci ZERO_NULL, /* doing */ 11113498266Sopenharmony_ci ZERO_NULL, /* proto_getsock */ 11213498266Sopenharmony_ci ZERO_NULL, /* doing_getsock */ 11313498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 11413498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 11513498266Sopenharmony_ci file_disconnect, /* disconnect */ 11613498266Sopenharmony_ci ZERO_NULL, /* write_resp */ 11713498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 11813498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 11913498266Sopenharmony_ci 0, /* defport */ 12013498266Sopenharmony_ci CURLPROTO_FILE, /* protocol */ 12113498266Sopenharmony_ci CURLPROTO_FILE, /* family */ 12213498266Sopenharmony_ci PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */ 12313498266Sopenharmony_ci}; 12413498266Sopenharmony_ci 12513498266Sopenharmony_ci 12613498266Sopenharmony_cistatic CURLcode file_setup_connection(struct Curl_easy *data, 12713498266Sopenharmony_ci struct connectdata *conn) 12813498266Sopenharmony_ci{ 12913498266Sopenharmony_ci (void)conn; 13013498266Sopenharmony_ci /* allocate the FILE specific struct */ 13113498266Sopenharmony_ci data->req.p.file = calloc(1, sizeof(struct FILEPROTO)); 13213498266Sopenharmony_ci if(!data->req.p.file) 13313498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 13413498266Sopenharmony_ci 13513498266Sopenharmony_ci return CURLE_OK; 13613498266Sopenharmony_ci} 13713498266Sopenharmony_ci 13813498266Sopenharmony_ci/* 13913498266Sopenharmony_ci * file_connect() gets called from Curl_protocol_connect() to allow us to 14013498266Sopenharmony_ci * do protocol-specific actions at connect-time. We emulate a 14113498266Sopenharmony_ci * connect-then-transfer protocol and "connect" to the file here 14213498266Sopenharmony_ci */ 14313498266Sopenharmony_cistatic CURLcode file_connect(struct Curl_easy *data, bool *done) 14413498266Sopenharmony_ci{ 14513498266Sopenharmony_ci char *real_path; 14613498266Sopenharmony_ci struct FILEPROTO *file = data->req.p.file; 14713498266Sopenharmony_ci int fd; 14813498266Sopenharmony_ci#ifdef DOS_FILESYSTEM 14913498266Sopenharmony_ci size_t i; 15013498266Sopenharmony_ci char *actual_path; 15113498266Sopenharmony_ci#endif 15213498266Sopenharmony_ci size_t real_path_len; 15313498266Sopenharmony_ci CURLcode result; 15413498266Sopenharmony_ci 15513498266Sopenharmony_ci if(file->path) { 15613498266Sopenharmony_ci /* already connected. 15713498266Sopenharmony_ci * the handler->connect_it() is normally only called once, but 15813498266Sopenharmony_ci * FILE does a special check on setting up the connection which 15913498266Sopenharmony_ci * calls this explicitly. */ 16013498266Sopenharmony_ci *done = TRUE; 16113498266Sopenharmony_ci return CURLE_OK; 16213498266Sopenharmony_ci } 16313498266Sopenharmony_ci 16413498266Sopenharmony_ci result = Curl_urldecode(data->state.up.path, 0, &real_path, 16513498266Sopenharmony_ci &real_path_len, REJECT_ZERO); 16613498266Sopenharmony_ci if(result) 16713498266Sopenharmony_ci return result; 16813498266Sopenharmony_ci 16913498266Sopenharmony_ci#ifdef DOS_FILESYSTEM 17013498266Sopenharmony_ci /* If the first character is a slash, and there's 17113498266Sopenharmony_ci something that looks like a drive at the beginning of 17213498266Sopenharmony_ci the path, skip the slash. If we remove the initial 17313498266Sopenharmony_ci slash in all cases, paths without drive letters end up 17413498266Sopenharmony_ci relative to the current directory which isn't how 17513498266Sopenharmony_ci browsers work. 17613498266Sopenharmony_ci 17713498266Sopenharmony_ci Some browsers accept | instead of : as the drive letter 17813498266Sopenharmony_ci separator, so we do too. 17913498266Sopenharmony_ci 18013498266Sopenharmony_ci On other platforms, we need the slash to indicate an 18113498266Sopenharmony_ci absolute pathname. On Windows, absolute paths start 18213498266Sopenharmony_ci with a drive letter. 18313498266Sopenharmony_ci */ 18413498266Sopenharmony_ci actual_path = real_path; 18513498266Sopenharmony_ci if((actual_path[0] == '/') && 18613498266Sopenharmony_ci actual_path[1] && 18713498266Sopenharmony_ci (actual_path[2] == ':' || actual_path[2] == '|')) { 18813498266Sopenharmony_ci actual_path[2] = ':'; 18913498266Sopenharmony_ci actual_path++; 19013498266Sopenharmony_ci real_path_len--; 19113498266Sopenharmony_ci } 19213498266Sopenharmony_ci 19313498266Sopenharmony_ci /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */ 19413498266Sopenharmony_ci for(i = 0; i < real_path_len; ++i) 19513498266Sopenharmony_ci if(actual_path[i] == '/') 19613498266Sopenharmony_ci actual_path[i] = '\\'; 19713498266Sopenharmony_ci else if(!actual_path[i]) { /* binary zero */ 19813498266Sopenharmony_ci Curl_safefree(real_path); 19913498266Sopenharmony_ci return CURLE_URL_MALFORMAT; 20013498266Sopenharmony_ci } 20113498266Sopenharmony_ci 20213498266Sopenharmony_ci fd = open_readonly(actual_path, O_RDONLY|O_BINARY); 20313498266Sopenharmony_ci file->path = actual_path; 20413498266Sopenharmony_ci#else 20513498266Sopenharmony_ci if(memchr(real_path, 0, real_path_len)) { 20613498266Sopenharmony_ci /* binary zeroes indicate foul play */ 20713498266Sopenharmony_ci Curl_safefree(real_path); 20813498266Sopenharmony_ci return CURLE_URL_MALFORMAT; 20913498266Sopenharmony_ci } 21013498266Sopenharmony_ci 21113498266Sopenharmony_ci #ifdef AMIGA_FILESYSTEM 21213498266Sopenharmony_ci /* 21313498266Sopenharmony_ci * A leading slash in an AmigaDOS path denotes the parent 21413498266Sopenharmony_ci * directory, and hence we block this as it is relative. 21513498266Sopenharmony_ci * Absolute paths start with 'volumename:', so we check for 21613498266Sopenharmony_ci * this first. Failing that, we treat the path as a real unix 21713498266Sopenharmony_ci * path, but only if the application was compiled with -lunix. 21813498266Sopenharmony_ci */ 21913498266Sopenharmony_ci fd = -1; 22013498266Sopenharmony_ci file->path = real_path; 22113498266Sopenharmony_ci 22213498266Sopenharmony_ci if(real_path[0] == '/') { 22313498266Sopenharmony_ci extern int __unix_path_semantics; 22413498266Sopenharmony_ci if(strchr(real_path + 1, ':')) { 22513498266Sopenharmony_ci /* Amiga absolute path */ 22613498266Sopenharmony_ci fd = open_readonly(real_path + 1, O_RDONLY); 22713498266Sopenharmony_ci file->path++; 22813498266Sopenharmony_ci } 22913498266Sopenharmony_ci else if(__unix_path_semantics) { 23013498266Sopenharmony_ci /* -lunix fallback */ 23113498266Sopenharmony_ci fd = open_readonly(real_path, O_RDONLY); 23213498266Sopenharmony_ci } 23313498266Sopenharmony_ci } 23413498266Sopenharmony_ci #else 23513498266Sopenharmony_ci fd = open_readonly(real_path, O_RDONLY); 23613498266Sopenharmony_ci file->path = real_path; 23713498266Sopenharmony_ci #endif 23813498266Sopenharmony_ci#endif 23913498266Sopenharmony_ci Curl_safefree(file->freepath); 24013498266Sopenharmony_ci file->freepath = real_path; /* free this when done */ 24113498266Sopenharmony_ci 24213498266Sopenharmony_ci file->fd = fd; 24313498266Sopenharmony_ci if(!data->state.upload && (fd == -1)) { 24413498266Sopenharmony_ci failf(data, "Couldn't open file %s", data->state.up.path); 24513498266Sopenharmony_ci file_done(data, CURLE_FILE_COULDNT_READ_FILE, FALSE); 24613498266Sopenharmony_ci return CURLE_FILE_COULDNT_READ_FILE; 24713498266Sopenharmony_ci } 24813498266Sopenharmony_ci *done = TRUE; 24913498266Sopenharmony_ci 25013498266Sopenharmony_ci return CURLE_OK; 25113498266Sopenharmony_ci} 25213498266Sopenharmony_ci 25313498266Sopenharmony_cistatic CURLcode file_done(struct Curl_easy *data, 25413498266Sopenharmony_ci CURLcode status, bool premature) 25513498266Sopenharmony_ci{ 25613498266Sopenharmony_ci struct FILEPROTO *file = data->req.p.file; 25713498266Sopenharmony_ci (void)status; /* not used */ 25813498266Sopenharmony_ci (void)premature; /* not used */ 25913498266Sopenharmony_ci 26013498266Sopenharmony_ci if(file) { 26113498266Sopenharmony_ci Curl_safefree(file->freepath); 26213498266Sopenharmony_ci file->path = NULL; 26313498266Sopenharmony_ci if(file->fd != -1) 26413498266Sopenharmony_ci close(file->fd); 26513498266Sopenharmony_ci file->fd = -1; 26613498266Sopenharmony_ci } 26713498266Sopenharmony_ci 26813498266Sopenharmony_ci return CURLE_OK; 26913498266Sopenharmony_ci} 27013498266Sopenharmony_ci 27113498266Sopenharmony_cistatic CURLcode file_disconnect(struct Curl_easy *data, 27213498266Sopenharmony_ci struct connectdata *conn, 27313498266Sopenharmony_ci bool dead_connection) 27413498266Sopenharmony_ci{ 27513498266Sopenharmony_ci (void)dead_connection; /* not used */ 27613498266Sopenharmony_ci (void)conn; 27713498266Sopenharmony_ci return file_done(data, CURLE_OK, FALSE); 27813498266Sopenharmony_ci} 27913498266Sopenharmony_ci 28013498266Sopenharmony_ci#ifdef DOS_FILESYSTEM 28113498266Sopenharmony_ci#define DIRSEP '\\' 28213498266Sopenharmony_ci#else 28313498266Sopenharmony_ci#define DIRSEP '/' 28413498266Sopenharmony_ci#endif 28513498266Sopenharmony_ci 28613498266Sopenharmony_cistatic CURLcode file_upload(struct Curl_easy *data) 28713498266Sopenharmony_ci{ 28813498266Sopenharmony_ci struct FILEPROTO *file = data->req.p.file; 28913498266Sopenharmony_ci const char *dir = strchr(file->path, DIRSEP); 29013498266Sopenharmony_ci int fd; 29113498266Sopenharmony_ci int mode; 29213498266Sopenharmony_ci CURLcode result = CURLE_OK; 29313498266Sopenharmony_ci char buffer[8*1024], *uphere_save; 29413498266Sopenharmony_ci curl_off_t bytecount = 0; 29513498266Sopenharmony_ci struct_stat file_stat; 29613498266Sopenharmony_ci const char *sendbuf; 29713498266Sopenharmony_ci 29813498266Sopenharmony_ci /* 29913498266Sopenharmony_ci * Since FILE: doesn't do the full init, we need to provide some extra 30013498266Sopenharmony_ci * assignments here. 30113498266Sopenharmony_ci */ 30213498266Sopenharmony_ci 30313498266Sopenharmony_ci if(!dir) 30413498266Sopenharmony_ci return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */ 30513498266Sopenharmony_ci 30613498266Sopenharmony_ci if(!dir[1]) 30713498266Sopenharmony_ci return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */ 30813498266Sopenharmony_ci 30913498266Sopenharmony_ci#ifdef O_BINARY 31013498266Sopenharmony_ci#define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY 31113498266Sopenharmony_ci#else 31213498266Sopenharmony_ci#define MODE_DEFAULT O_WRONLY|O_CREAT 31313498266Sopenharmony_ci#endif 31413498266Sopenharmony_ci 31513498266Sopenharmony_ci if(data->state.resume_from) 31613498266Sopenharmony_ci mode = MODE_DEFAULT|O_APPEND; 31713498266Sopenharmony_ci else 31813498266Sopenharmony_ci mode = MODE_DEFAULT|O_TRUNC; 31913498266Sopenharmony_ci 32013498266Sopenharmony_ci fd = open(file->path, mode, data->set.new_file_perms); 32113498266Sopenharmony_ci if(fd < 0) { 32213498266Sopenharmony_ci failf(data, "Can't open %s for writing", file->path); 32313498266Sopenharmony_ci return CURLE_WRITE_ERROR; 32413498266Sopenharmony_ci } 32513498266Sopenharmony_ci 32613498266Sopenharmony_ci if(-1 != data->state.infilesize) 32713498266Sopenharmony_ci /* known size of data to "upload" */ 32813498266Sopenharmony_ci Curl_pgrsSetUploadSize(data, data->state.infilesize); 32913498266Sopenharmony_ci 33013498266Sopenharmony_ci /* treat the negative resume offset value as the case of "-" */ 33113498266Sopenharmony_ci if(data->state.resume_from < 0) { 33213498266Sopenharmony_ci if(fstat(fd, &file_stat)) { 33313498266Sopenharmony_ci close(fd); 33413498266Sopenharmony_ci failf(data, "Can't get the size of %s", file->path); 33513498266Sopenharmony_ci return CURLE_WRITE_ERROR; 33613498266Sopenharmony_ci } 33713498266Sopenharmony_ci data->state.resume_from = (curl_off_t)file_stat.st_size; 33813498266Sopenharmony_ci } 33913498266Sopenharmony_ci 34013498266Sopenharmony_ci /* Yikes! Curl_fillreadbuffer uses data->req.upload_fromhere to READ 34113498266Sopenharmony_ci * client data to! Please, someone fix... */ 34213498266Sopenharmony_ci uphere_save = data->req.upload_fromhere; 34313498266Sopenharmony_ci while(!result) { 34413498266Sopenharmony_ci size_t nread; 34513498266Sopenharmony_ci ssize_t nwrite; 34613498266Sopenharmony_ci size_t readcount; 34713498266Sopenharmony_ci data->req.upload_fromhere = buffer; 34813498266Sopenharmony_ci result = Curl_fillreadbuffer(data, sizeof(buffer), &readcount); 34913498266Sopenharmony_ci if(result) 35013498266Sopenharmony_ci break; 35113498266Sopenharmony_ci 35213498266Sopenharmony_ci if(!readcount) 35313498266Sopenharmony_ci break; 35413498266Sopenharmony_ci 35513498266Sopenharmony_ci nread = readcount; 35613498266Sopenharmony_ci 35713498266Sopenharmony_ci /* skip bytes before resume point */ 35813498266Sopenharmony_ci if(data->state.resume_from) { 35913498266Sopenharmony_ci if((curl_off_t)nread <= data->state.resume_from) { 36013498266Sopenharmony_ci data->state.resume_from -= nread; 36113498266Sopenharmony_ci nread = 0; 36213498266Sopenharmony_ci sendbuf = buffer; 36313498266Sopenharmony_ci } 36413498266Sopenharmony_ci else { 36513498266Sopenharmony_ci sendbuf = buffer + data->state.resume_from; 36613498266Sopenharmony_ci nread -= (size_t)data->state.resume_from; 36713498266Sopenharmony_ci data->state.resume_from = 0; 36813498266Sopenharmony_ci } 36913498266Sopenharmony_ci } 37013498266Sopenharmony_ci else 37113498266Sopenharmony_ci sendbuf = buffer; 37213498266Sopenharmony_ci 37313498266Sopenharmony_ci /* write the data to the target */ 37413498266Sopenharmony_ci nwrite = write(fd, sendbuf, nread); 37513498266Sopenharmony_ci if((size_t)nwrite != nread) { 37613498266Sopenharmony_ci result = CURLE_SEND_ERROR; 37713498266Sopenharmony_ci break; 37813498266Sopenharmony_ci } 37913498266Sopenharmony_ci 38013498266Sopenharmony_ci bytecount += nread; 38113498266Sopenharmony_ci 38213498266Sopenharmony_ci Curl_pgrsSetUploadCounter(data, bytecount); 38313498266Sopenharmony_ci 38413498266Sopenharmony_ci if(Curl_pgrsUpdate(data)) 38513498266Sopenharmony_ci result = CURLE_ABORTED_BY_CALLBACK; 38613498266Sopenharmony_ci else 38713498266Sopenharmony_ci result = Curl_speedcheck(data, Curl_now()); 38813498266Sopenharmony_ci } 38913498266Sopenharmony_ci if(!result && Curl_pgrsUpdate(data)) 39013498266Sopenharmony_ci result = CURLE_ABORTED_BY_CALLBACK; 39113498266Sopenharmony_ci 39213498266Sopenharmony_ci close(fd); 39313498266Sopenharmony_ci data->req.upload_fromhere = uphere_save; 39413498266Sopenharmony_ci 39513498266Sopenharmony_ci return result; 39613498266Sopenharmony_ci} 39713498266Sopenharmony_ci 39813498266Sopenharmony_ci/* 39913498266Sopenharmony_ci * file_do() is the protocol-specific function for the do-phase, separated 40013498266Sopenharmony_ci * from the connect-phase above. Other protocols merely setup the transfer in 40113498266Sopenharmony_ci * the do-phase, to have it done in the main transfer loop but since some 40213498266Sopenharmony_ci * platforms we support don't allow select()ing etc on file handles (as 40313498266Sopenharmony_ci * opposed to sockets) we instead perform the whole do-operation in this 40413498266Sopenharmony_ci * function. 40513498266Sopenharmony_ci */ 40613498266Sopenharmony_cistatic CURLcode file_do(struct Curl_easy *data, bool *done) 40713498266Sopenharmony_ci{ 40813498266Sopenharmony_ci /* This implementation ignores the host name in conformance with 40913498266Sopenharmony_ci RFC 1738. Only local files (reachable via the standard file system) 41013498266Sopenharmony_ci are supported. This means that files on remotely mounted directories 41113498266Sopenharmony_ci (via NFS, Samba, NT sharing) can be accessed through a file:// URL 41213498266Sopenharmony_ci */ 41313498266Sopenharmony_ci CURLcode result = CURLE_OK; 41413498266Sopenharmony_ci struct_stat statbuf; /* struct_stat instead of struct stat just to allow the 41513498266Sopenharmony_ci Windows version to have a different struct without 41613498266Sopenharmony_ci having to redefine the simple word 'stat' */ 41713498266Sopenharmony_ci curl_off_t expected_size = -1; 41813498266Sopenharmony_ci bool size_known; 41913498266Sopenharmony_ci bool fstated = FALSE; 42013498266Sopenharmony_ci int fd; 42113498266Sopenharmony_ci struct FILEPROTO *file; 42213498266Sopenharmony_ci 42313498266Sopenharmony_ci *done = TRUE; /* unconditionally */ 42413498266Sopenharmony_ci 42513498266Sopenharmony_ci if(data->state.upload) 42613498266Sopenharmony_ci return file_upload(data); 42713498266Sopenharmony_ci 42813498266Sopenharmony_ci file = data->req.p.file; 42913498266Sopenharmony_ci 43013498266Sopenharmony_ci /* get the fd from the connection phase */ 43113498266Sopenharmony_ci fd = file->fd; 43213498266Sopenharmony_ci 43313498266Sopenharmony_ci /* VMS: This only works reliable for STREAMLF files */ 43413498266Sopenharmony_ci if(-1 != fstat(fd, &statbuf)) { 43513498266Sopenharmony_ci if(!S_ISDIR(statbuf.st_mode)) 43613498266Sopenharmony_ci expected_size = statbuf.st_size; 43713498266Sopenharmony_ci /* and store the modification time */ 43813498266Sopenharmony_ci data->info.filetime = statbuf.st_mtime; 43913498266Sopenharmony_ci fstated = TRUE; 44013498266Sopenharmony_ci } 44113498266Sopenharmony_ci 44213498266Sopenharmony_ci if(fstated && !data->state.range && data->set.timecondition) { 44313498266Sopenharmony_ci if(!Curl_meets_timecondition(data, data->info.filetime)) { 44413498266Sopenharmony_ci *done = TRUE; 44513498266Sopenharmony_ci return CURLE_OK; 44613498266Sopenharmony_ci } 44713498266Sopenharmony_ci } 44813498266Sopenharmony_ci 44913498266Sopenharmony_ci if(fstated) { 45013498266Sopenharmony_ci time_t filetime; 45113498266Sopenharmony_ci struct tm buffer; 45213498266Sopenharmony_ci const struct tm *tm = &buffer; 45313498266Sopenharmony_ci char header[80]; 45413498266Sopenharmony_ci int headerlen; 45513498266Sopenharmony_ci char accept_ranges[24]= { "Accept-ranges: bytes\r\n" }; 45613498266Sopenharmony_ci if(expected_size >= 0) { 45713498266Sopenharmony_ci headerlen = msnprintf(header, sizeof(header), 45813498266Sopenharmony_ci "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", 45913498266Sopenharmony_ci expected_size); 46013498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen); 46113498266Sopenharmony_ci if(result) 46213498266Sopenharmony_ci return result; 46313498266Sopenharmony_ci 46413498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_HEADER, 46513498266Sopenharmony_ci accept_ranges, strlen(accept_ranges)); 46613498266Sopenharmony_ci if(result != CURLE_OK) 46713498266Sopenharmony_ci return result; 46813498266Sopenharmony_ci } 46913498266Sopenharmony_ci 47013498266Sopenharmony_ci filetime = (time_t)statbuf.st_mtime; 47113498266Sopenharmony_ci result = Curl_gmtime(filetime, &buffer); 47213498266Sopenharmony_ci if(result) 47313498266Sopenharmony_ci return result; 47413498266Sopenharmony_ci 47513498266Sopenharmony_ci /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ 47613498266Sopenharmony_ci headerlen = msnprintf(header, sizeof(header), 47713498266Sopenharmony_ci "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n%s", 47813498266Sopenharmony_ci Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], 47913498266Sopenharmony_ci tm->tm_mday, 48013498266Sopenharmony_ci Curl_month[tm->tm_mon], 48113498266Sopenharmony_ci tm->tm_year + 1900, 48213498266Sopenharmony_ci tm->tm_hour, 48313498266Sopenharmony_ci tm->tm_min, 48413498266Sopenharmony_ci tm->tm_sec, 48513498266Sopenharmony_ci data->req.no_body ? "": "\r\n"); 48613498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen); 48713498266Sopenharmony_ci if(result) 48813498266Sopenharmony_ci return result; 48913498266Sopenharmony_ci /* set the file size to make it available post transfer */ 49013498266Sopenharmony_ci Curl_pgrsSetDownloadSize(data, expected_size); 49113498266Sopenharmony_ci if(data->req.no_body) 49213498266Sopenharmony_ci return result; 49313498266Sopenharmony_ci } 49413498266Sopenharmony_ci 49513498266Sopenharmony_ci /* Check whether file range has been specified */ 49613498266Sopenharmony_ci result = Curl_range(data); 49713498266Sopenharmony_ci if(result) 49813498266Sopenharmony_ci return result; 49913498266Sopenharmony_ci 50013498266Sopenharmony_ci /* Adjust the start offset in case we want to get the N last bytes 50113498266Sopenharmony_ci * of the stream if the filesize could be determined */ 50213498266Sopenharmony_ci if(data->state.resume_from < 0) { 50313498266Sopenharmony_ci if(!fstated) { 50413498266Sopenharmony_ci failf(data, "Can't get the size of file."); 50513498266Sopenharmony_ci return CURLE_READ_ERROR; 50613498266Sopenharmony_ci } 50713498266Sopenharmony_ci data->state.resume_from += (curl_off_t)statbuf.st_size; 50813498266Sopenharmony_ci } 50913498266Sopenharmony_ci 51013498266Sopenharmony_ci if(data->state.resume_from > 0) { 51113498266Sopenharmony_ci /* We check explicitly if we have a start offset, because 51213498266Sopenharmony_ci * expected_size may be -1 if we don't know how large the file is, 51313498266Sopenharmony_ci * in which case we should not adjust it. */ 51413498266Sopenharmony_ci if(data->state.resume_from <= expected_size) 51513498266Sopenharmony_ci expected_size -= data->state.resume_from; 51613498266Sopenharmony_ci else { 51713498266Sopenharmony_ci failf(data, "failed to resume file:// transfer"); 51813498266Sopenharmony_ci return CURLE_BAD_DOWNLOAD_RESUME; 51913498266Sopenharmony_ci } 52013498266Sopenharmony_ci } 52113498266Sopenharmony_ci 52213498266Sopenharmony_ci /* A high water mark has been specified so we obey... */ 52313498266Sopenharmony_ci if(data->req.maxdownload > 0) 52413498266Sopenharmony_ci expected_size = data->req.maxdownload; 52513498266Sopenharmony_ci 52613498266Sopenharmony_ci if(!fstated || (expected_size <= 0)) 52713498266Sopenharmony_ci size_known = FALSE; 52813498266Sopenharmony_ci else 52913498266Sopenharmony_ci size_known = TRUE; 53013498266Sopenharmony_ci 53113498266Sopenharmony_ci /* The following is a shortcut implementation of file reading 53213498266Sopenharmony_ci this is both more efficient than the former call to download() and 53313498266Sopenharmony_ci it avoids problems with select() and recv() on file descriptors 53413498266Sopenharmony_ci in Winsock */ 53513498266Sopenharmony_ci if(size_known) 53613498266Sopenharmony_ci Curl_pgrsSetDownloadSize(data, expected_size); 53713498266Sopenharmony_ci 53813498266Sopenharmony_ci if(data->state.resume_from) { 53913498266Sopenharmony_ci if(data->state.resume_from != 54013498266Sopenharmony_ci lseek(fd, data->state.resume_from, SEEK_SET)) 54113498266Sopenharmony_ci return CURLE_BAD_DOWNLOAD_RESUME; 54213498266Sopenharmony_ci } 54313498266Sopenharmony_ci 54413498266Sopenharmony_ci Curl_pgrsTime(data, TIMER_STARTTRANSFER); 54513498266Sopenharmony_ci 54613498266Sopenharmony_ci while(!result) { 54713498266Sopenharmony_ci char tmpbuf[8*1024]; 54813498266Sopenharmony_ci ssize_t nread; 54913498266Sopenharmony_ci /* Don't fill a whole buffer if we want less than all data */ 55013498266Sopenharmony_ci size_t bytestoread; 55113498266Sopenharmony_ci 55213498266Sopenharmony_ci if(size_known) { 55313498266Sopenharmony_ci bytestoread = (expected_size < (curl_off_t)(sizeof(tmpbuf)-1)) ? 55413498266Sopenharmony_ci curlx_sotouz(expected_size) : (sizeof(tmpbuf)-1); 55513498266Sopenharmony_ci } 55613498266Sopenharmony_ci else 55713498266Sopenharmony_ci bytestoread = sizeof(tmpbuf)-1; 55813498266Sopenharmony_ci 55913498266Sopenharmony_ci nread = read(fd, tmpbuf, bytestoread); 56013498266Sopenharmony_ci 56113498266Sopenharmony_ci if(nread > 0) 56213498266Sopenharmony_ci tmpbuf[nread] = 0; 56313498266Sopenharmony_ci 56413498266Sopenharmony_ci if(nread <= 0 || (size_known && (expected_size == 0))) 56513498266Sopenharmony_ci break; 56613498266Sopenharmony_ci 56713498266Sopenharmony_ci if(size_known) 56813498266Sopenharmony_ci expected_size -= nread; 56913498266Sopenharmony_ci 57013498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_BODY, tmpbuf, nread); 57113498266Sopenharmony_ci if(result) 57213498266Sopenharmony_ci return result; 57313498266Sopenharmony_ci 57413498266Sopenharmony_ci if(Curl_pgrsUpdate(data)) 57513498266Sopenharmony_ci result = CURLE_ABORTED_BY_CALLBACK; 57613498266Sopenharmony_ci else 57713498266Sopenharmony_ci result = Curl_speedcheck(data, Curl_now()); 57813498266Sopenharmony_ci } 57913498266Sopenharmony_ci if(Curl_pgrsUpdate(data)) 58013498266Sopenharmony_ci result = CURLE_ABORTED_BY_CALLBACK; 58113498266Sopenharmony_ci 58213498266Sopenharmony_ci return result; 58313498266Sopenharmony_ci} 58413498266Sopenharmony_ci 58513498266Sopenharmony_ci#endif 586