blob: 0bbc0e1800226c8d720aa8d3cacfcb0916714430 [file] [log] [blame]
Kristian Monsen5ab50182010-05-14 18:53:44 +01001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
Elliott Hughes82be86d2017-09-20 17:00:17 -07008 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
Kristian Monsen5ab50182010-05-14 18:53:44 +01009 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
Alex Deymod15eaac2016-06-28 14:49:26 -070012 * are also available at https://curl.haxx.se/docs/copyright.html.
Kristian Monsen5ab50182010-05-14 18:53:44 +010013 *
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
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070023#include "curl_setup.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010024
25#ifndef CURL_DISABLE_FILE
Kristian Monsen5ab50182010-05-14 18:53:44 +010026
Kristian Monsen5ab50182010-05-14 18:53:44 +010027#ifdef HAVE_NETINET_IN_H
28#include <netinet/in.h>
29#endif
Kristian Monsen5ab50182010-05-14 18:53:44 +010030#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
Kristian Monsen5ab50182010-05-14 18:53:44 +010051#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"
Kristian Monsen5ab50182010-05-14 18:53:44 +010062#include "parsedate.h" /* for the week day and month names */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070063#include "warnless.h"
Alex Deymod15eaac2016-06-28 14:49:26 -070064/* The last 3 #include files should be in this order */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070065#include "curl_printf.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070066#include "curl_memory.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010067#include "memdebug.h"
68
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070069#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \
70 defined(__SYMBIAN32__)
Kristian Monsen5ab50182010-05-14 18:53:44 +010071#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
84static CURLcode file_do(struct connectdata *, bool *done);
85static CURLcode file_done(struct connectdata *conn,
86 CURLcode status, bool premature);
87static CURLcode file_connect(struct connectdata *conn, bool *done);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070088static CURLcode file_disconnect(struct connectdata *conn,
89 bool dead_connection);
90static CURLcode file_setup_connection(struct connectdata *conn);
Kristian Monsen5ab50182010-05-14 18:53:44 +010091
92/*
93 * FILE scheme handler.
94 */
95
96const struct Curl_handler Curl_handler_file = {
97 "FILE", /* scheme */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070098 file_setup_connection, /* setup_connection */
Kristian Monsen5ab50182010-05-14 18:53:44 +010099 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 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700107 ZERO_NULL, /* domore_getsock */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100108 ZERO_NULL, /* perform_getsock */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700109 file_disconnect, /* disconnect */
110 ZERO_NULL, /* readwrite */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700111 ZERO_NULL, /* connection_check */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100112 0, /* defport */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700113 CURLPROTO_FILE, /* protocol */
114 PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100115};
116
117
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700118static 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
Kristian Monsen5ab50182010-05-14 18:53:44 +0100128 /*
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 */
133static CURLcode file_range(struct connectdata *conn)
134{
135 curl_off_t from, to;
Alex Deymo486467e2017-12-19 19:04:07 +0100136 curl_off_t totalsize = -1;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100137 char *ptr;
138 char *ptr2;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700139 struct Curl_easy *data = conn->data;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100140
141 if(data->state.use_range && data->state.range) {
Alex Deymo486467e2017-12-19 19:04:07 +0100142 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 == '-')))
Kristian Monsen5ab50182010-05-14 18:53:44 +0100148 ptr++;
Alex Deymo486467e2017-12-19 19:04:07 +0100149 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) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100153 /* X - */
154 data->state.resume_from = from;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700155 DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file\n",
Kristian Monsen5ab50182010-05-14 18:53:44 +0100156 from));
157 }
Alex Deymo486467e2017-12-19 19:04:07 +0100158 else if((from_t == CURL_OFFT_INVAL) && !to_t) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100159 /* -Y */
Alex Deymo486467e2017-12-19 19:04:07 +0100160 data->req.maxdownload = to;
161 data->state.resume_from = -to;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700162 DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes\n",
Alex Deymo486467e2017-12-19 19:04:07 +0100163 to));
Kristian Monsen5ab50182010-05-14 18:53:44 +0100164 }
165 else {
166 /* X-Y */
167 totalsize = to-from;
Alex Deymo486467e2017-12-19 19:04:07 +0100168 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 */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100172 data->state.resume_from = from;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700173 DEBUGF(infof(data, "RANGE from %" CURL_FORMAT_CURL_OFF_T
174 " getting %" CURL_FORMAT_CURL_OFF_T " bytes\n",
Kristian Monsen5ab50182010-05-14 18:53:44 +0100175 from, data->req.maxdownload));
176 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700177 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",
Kristian Monsen5ab50182010-05-14 18:53:44 +0100180 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 */
192static CURLcode file_connect(struct connectdata *conn, bool *done)
193{
Alex Deymoe3149cc2016-10-05 11:18:42 -0700194 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700195 char *real_path;
196 struct FILEPROTO *file = data->req.protop;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100197 int fd;
198#ifdef DOS_FILESYSTEM
Elliott Hughescee03382017-06-23 12:17:18 -0700199 size_t i;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100200 char *actual_path;
201#endif
Elliott Hughescee03382017-06-23 12:17:18 -0700202 size_t real_path_len;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100203
Elliott Hughescee03382017-06-23 12:17:18 -0700204 CURLcode result = Curl_urldecode(data, data->state.path, 0, &real_path,
205 &real_path_len, FALSE);
206 if(result)
207 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100208
Kristian Monsen5ab50182010-05-14 18:53:44 +0100209#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] &&
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700227 (actual_path[2] == ':' || actual_path[2] == '|')) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100228 actual_path[2] = ':';
229 actual_path++;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700230 real_path_len--;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100231 }
232
233 /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
Alex Deymo486467e2017-12-19 19:04:07 +0100234 for(i = 0; i < real_path_len; ++i)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100235 if(actual_path[i] == '/')
236 actual_path[i] = '\\';
Alex Deymoe3149cc2016-10-05 11:18:42 -0700237 else if(!actual_path[i]) { /* binary zero */
238 Curl_safefree(real_path);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700239 return CURLE_URL_MALFORMAT;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700240 }
Kristian Monsen5ab50182010-05-14 18:53:44 +0100241
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700242 fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100243 file->path = actual_path;
244#else
Alex Deymoe3149cc2016-10-05 11:18:42 -0700245 if(memchr(real_path, 0, real_path_len)) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700246 /* binary zeroes indicate foul play */
Alex Deymoe3149cc2016-10-05 11:18:42 -0700247 Curl_safefree(real_path);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700248 return CURLE_URL_MALFORMAT;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700249 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700250
Kristian Monsen5ab50182010-05-14 18:53:44 +0100251 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
267static CURLcode file_done(struct connectdata *conn,
268 CURLcode status, bool premature)
269{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700270 struct FILEPROTO *file = conn->data->req.protop;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100271 (void)status; /* not used */
272 (void)premature; /* not used */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100273
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700274 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
285static 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 }
Kristian Monsen5ab50182010-05-14 18:53:44 +0100298
299 return CURLE_OK;
300}
301
302#ifdef DOS_FILESYSTEM
303#define DIRSEP '\\'
304#else
305#define DIRSEP '/'
306#endif
307
308static CURLcode file_upload(struct connectdata *conn)
309{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700310 struct FILEPROTO *file = conn->data->req.protop;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100311 const char *dir = strchr(file->path, DIRSEP);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700312 int fd;
313 int mode;
314 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700315 struct Curl_easy *data = conn->data;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100316 char *buf = data->state.buffer;
317 size_t nread;
318 size_t nwrite;
319 curl_off_t bytecount = 0;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100320 struct_stat file_stat;
Elliott Hughescee03382017-06-23 12:17:18 -0700321 const char *buf2;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100322
323 /*
324 * Since FILE: doesn't do the full init, we need to provide some extra
325 * assignments here.
326 */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100327 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])
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700333 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
Kristian Monsen5ab50182010-05-14 18:53:44 +0100340
341 if(data->state.resume_from)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700342 mode = MODE_DEFAULT|O_APPEND;
343 else
344 mode = MODE_DEFAULT|O_TRUNC;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100345
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700346 fd = open(file->path, mode, conn->data->set.new_file_perms);
347 if(fd < 0) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100348 failf(data, "Can't open %s for writing", file->path);
349 return CURLE_WRITE_ERROR;
350 }
351
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700352 if(-1 != data->state.infilesize)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100353 /* known size of data to "upload" */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700354 Curl_pgrsSetUploadSize(data, data->state.infilesize);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100355
356 /* treat the negative resume offset value as the case of "-" */
357 if(data->state.resume_from < 0) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700358 if(fstat(fd, &file_stat)) {
359 close(fd);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100360 failf(data, "Can't get the size of %s", file->path);
361 return CURLE_WRITE_ERROR;
362 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700363 data->state.resume_from = (curl_off_t)file_stat.st_size;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100364 }
365
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700366 while(!result) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100367 int readcount;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700368 result = Curl_fillreadbuffer(conn, (int)data->set.buffer_size, &readcount);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700369 if(result)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100370 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) {
Alex Deymod15eaac2016-06-28 14:49:26 -0700379 if((curl_off_t)nread <= data->state.resume_from) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100380 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 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700394 nwrite = write(fd, buf2, nread);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100395 if(nwrite != nread) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700396 result = CURLE_SEND_ERROR;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100397 break;
398 }
399
400 bytecount += nread;
401
402 Curl_pgrsSetUploadCounter(data, bytecount);
403
404 if(Curl_pgrsUpdate(conn))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700405 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100406 else
Alex Deymo486467e2017-12-19 19:04:07 +0100407 result = Curl_speedcheck(data, Curl_now());
Kristian Monsen5ab50182010-05-14 18:53:44 +0100408 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700409 if(!result && Curl_pgrsUpdate(conn))
410 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100411
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700412 close(fd);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100413
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700414 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100415}
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 */
425static 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 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700432 CURLcode result = CURLE_OK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100433 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' */
Alex Deymo486467e2017-12-19 19:04:07 +0100436 curl_off_t expected_size = 0;
Alex Deymod15eaac2016-06-28 14:49:26 -0700437 bool size_known;
Alex Deymo486467e2017-12-19 19:04:07 +0100438 bool fstated = FALSE;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100439 ssize_t nread;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700440 struct Curl_easy *data = conn->data;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100441 char *buf = data->state.buffer;
442 curl_off_t bytecount = 0;
443 int fd;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700444 struct FILEPROTO *file;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100445
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
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700454 file = conn->data->req.protop;
455
Kristian Monsen5ab50182010-05-14 18:53:44 +0100456 /* get the fd from the connection phase */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700457 fd = file->fd;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100458
459 /* VMS: This only works reliable for STREAMLF files */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700460 if(-1 != fstat(fd, &statbuf)) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100461 /* 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
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700468 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
Kristian Monsen5ab50182010-05-14 18:53:44 +0100475 /* 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) {
Alex Deymod15eaac2016-06-28 14:49:26 -0700479 time_t filetime;
480 struct tm buffer;
481 const struct tm *tm = &buffer;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700482 char header[80];
483 snprintf(header, sizeof(header),
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700484 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size);
Elliott Hughes82be86d2017-09-20 17:00:17 -0700485 result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100486 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
Alex Deymod15eaac2016-06-28 14:49:26 -0700494 filetime = (time_t)statbuf.st_mtime;
495 result = Curl_gmtime(filetime, &buffer);
496 if(result)
497 return result;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700498
Alex Deymod15eaac2016-06-28 14:49:26 -0700499 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700500 snprintf(header, sizeof(header),
Alex Deymod15eaac2016-06-28 14:49:26 -0700501 "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);
Elliott Hughes82be86d2017-09-20 17:00:17 -0700509 result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0);
Alex Deymod15eaac2016-06-28 14:49:26 -0700510 if(!result)
511 /* set the file size to make it available post transfer */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100512 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 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700526 data->state.resume_from += (curl_off_t)statbuf.st_size;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100527 }
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... */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700537 if(data->req.maxdownload > 0)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100538 expected_size = data->req.maxdownload;
539
Alex Deymod15eaac2016-06-28 14:49:26 -0700540 if(!fstated || (expected_size == 0))
541 size_known = FALSE;
542 else
543 size_known = TRUE;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100544
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
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700560 while(!result) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100561 /* Don't fill a whole buffer if we want less than all data */
Alex Deymod15eaac2016-06-28 14:49:26 -0700562 size_t bytestoread;
563
564 if(size_known) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700565 bytestoread = (expected_size < data->set.buffer_size) ?
566 curlx_sotouz(expected_size) : (size_t)data->set.buffer_size;
Alex Deymod15eaac2016-06-28 14:49:26 -0700567 }
568 else
Elliott Hughes82be86d2017-09-20 17:00:17 -0700569 bytestoread = data->set.buffer_size-1;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700570
Kristian Monsen5ab50182010-05-14 18:53:44 +0100571 nread = read(fd, buf, bytestoread);
572
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700573 if(nread > 0)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100574 buf[nread] = 0;
575
Alex Deymod15eaac2016-06-28 14:49:26 -0700576 if(nread <= 0 || (size_known && (expected_size == 0)))
Kristian Monsen5ab50182010-05-14 18:53:44 +0100577 break;
578
579 bytecount += nread;
Alex Deymod15eaac2016-06-28 14:49:26 -0700580 if(size_known)
581 expected_size -= nread;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100582
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700583 result = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
584 if(result)
585 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100586
587 Curl_pgrsSetDownloadCounter(data, bytecount);
588
589 if(Curl_pgrsUpdate(conn))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700590 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100591 else
Alex Deymo486467e2017-12-19 19:04:07 +0100592 result = Curl_speedcheck(data, Curl_now());
Kristian Monsen5ab50182010-05-14 18:53:44 +0100593 }
594 if(Curl_pgrsUpdate(conn))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700595 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100596
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700597 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100598}
599
600#endif