blob: 175b10731e59ddacf8c20136265787fcb7cea207 [file] [log] [blame]
Kristian Monsen5ab50182010-05-14 18:53:44 +01001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07008 * Copyright (C) 1998 - 2015, 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
12 * are also available at http://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
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"
64#include "curl_printf.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010065
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070066/* The last #include files should be: */
67#include "curl_memory.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010068#include "memdebug.h"
69
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070070#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \
71 defined(__SYMBIAN32__)
Kristian Monsen5ab50182010-05-14 18:53:44 +010072#define DOS_FILESYSTEM 1
73#endif
74
75#ifdef OPEN_NEEDS_ARG3
76# define open_readonly(p,f) open((p),(f),(0))
77#else
78# define open_readonly(p,f) open((p),(f))
79#endif
80
81/*
82 * Forward declarations.
83 */
84
85static CURLcode file_do(struct connectdata *, bool *done);
86static CURLcode file_done(struct connectdata *conn,
87 CURLcode status, bool premature);
88static CURLcode file_connect(struct connectdata *conn, bool *done);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070089static CURLcode file_disconnect(struct connectdata *conn,
90 bool dead_connection);
91static CURLcode file_setup_connection(struct connectdata *conn);
Kristian Monsen5ab50182010-05-14 18:53:44 +010092
93/*
94 * FILE scheme handler.
95 */
96
97const struct Curl_handler Curl_handler_file = {
98 "FILE", /* scheme */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070099 file_setup_connection, /* setup_connection */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100100 file_do, /* do_it */
101 file_done, /* done */
102 ZERO_NULL, /* do_more */
103 file_connect, /* connect_it */
104 ZERO_NULL, /* connecting */
105 ZERO_NULL, /* doing */
106 ZERO_NULL, /* proto_getsock */
107 ZERO_NULL, /* doing_getsock */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700108 ZERO_NULL, /* domore_getsock */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100109 ZERO_NULL, /* perform_getsock */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700110 file_disconnect, /* disconnect */
111 ZERO_NULL, /* readwrite */
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;
136 curl_off_t totalsize=-1;
137 char *ptr;
138 char *ptr2;
139 struct SessionHandle *data = conn->data;
140
141 if(data->state.use_range && data->state.range) {
142 from=curlx_strtoofft(data->state.range, &ptr, 0);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700143 while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
Kristian Monsen5ab50182010-05-14 18:53:44 +0100144 ptr++;
145 to=curlx_strtoofft(ptr, &ptr2, 0);
146 if(ptr == ptr2) {
147 /* we didn't get any digit */
148 to=-1;
149 }
150 if((-1 == to) && (from>=0)) {
151 /* X - */
152 data->state.resume_from = from;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700153 DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file\n",
Kristian Monsen5ab50182010-05-14 18:53:44 +0100154 from));
155 }
156 else if(from < 0) {
157 /* -Y */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100158 data->req.maxdownload = -from;
159 data->state.resume_from = from;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700160 DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes\n",
Lucas Eckels9bd90e62012-08-06 15:07:02 -0700161 -from));
Kristian Monsen5ab50182010-05-14 18:53:44 +0100162 }
163 else {
164 /* X-Y */
165 totalsize = to-from;
166 data->req.maxdownload = totalsize+1; /* include last byte */
167 data->state.resume_from = from;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700168 DEBUGF(infof(data, "RANGE from %" CURL_FORMAT_CURL_OFF_T
169 " getting %" CURL_FORMAT_CURL_OFF_T " bytes\n",
Kristian Monsen5ab50182010-05-14 18:53:44 +0100170 from, data->req.maxdownload));
171 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700172 DEBUGF(infof(data, "range-download from %" CURL_FORMAT_CURL_OFF_T
173 " to %" CURL_FORMAT_CURL_OFF_T ", totally %"
174 CURL_FORMAT_CURL_OFF_T " bytes\n",
Kristian Monsen5ab50182010-05-14 18:53:44 +0100175 from, to, data->req.maxdownload));
176 }
177 else
178 data->req.maxdownload = -1;
179 return CURLE_OK;
180}
181
182/*
183 * file_connect() gets called from Curl_protocol_connect() to allow us to
184 * do protocol-specific actions at connect-time. We emulate a
185 * connect-then-transfer protocol and "connect" to the file here
186 */
187static CURLcode file_connect(struct connectdata *conn, bool *done)
188{
189 struct SessionHandle *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700190 char *real_path;
191 struct FILEPROTO *file = data->req.protop;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100192 int fd;
193#ifdef DOS_FILESYSTEM
194 int i;
195 char *actual_path;
196#endif
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700197 int real_path_len;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100198
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700199 real_path = curl_easy_unescape(data, data->state.path, 0, &real_path_len);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100200 if(!real_path)
201 return CURLE_OUT_OF_MEMORY;
202
Kristian Monsen5ab50182010-05-14 18:53:44 +0100203#ifdef DOS_FILESYSTEM
204 /* If the first character is a slash, and there's
205 something that looks like a drive at the beginning of
206 the path, skip the slash. If we remove the initial
207 slash in all cases, paths without drive letters end up
208 relative to the current directory which isn't how
209 browsers work.
210
211 Some browsers accept | instead of : as the drive letter
212 separator, so we do too.
213
214 On other platforms, we need the slash to indicate an
215 absolute pathname. On Windows, absolute paths start
216 with a drive letter.
217 */
218 actual_path = real_path;
219 if((actual_path[0] == '/') &&
220 actual_path[1] &&
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700221 (actual_path[2] == ':' || actual_path[2] == '|')) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100222 actual_path[2] = ':';
223 actual_path++;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700224 real_path_len--;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100225 }
226
227 /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700228 for(i=0; i < real_path_len; ++i)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100229 if(actual_path[i] == '/')
230 actual_path[i] = '\\';
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700231 else if(!actual_path[i]) /* binary zero */
232 return CURLE_URL_MALFORMAT;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100233
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700234 fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100235 file->path = actual_path;
236#else
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700237 if(memchr(real_path, 0, real_path_len))
238 /* binary zeroes indicate foul play */
239 return CURLE_URL_MALFORMAT;
240
Kristian Monsen5ab50182010-05-14 18:53:44 +0100241 fd = open_readonly(real_path, O_RDONLY);
242 file->path = real_path;
243#endif
244 file->freepath = real_path; /* free this when done */
245
246 file->fd = fd;
247 if(!data->set.upload && (fd == -1)) {
248 failf(data, "Couldn't open file %s", data->state.path);
249 file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
250 return CURLE_FILE_COULDNT_READ_FILE;
251 }
252 *done = TRUE;
253
254 return CURLE_OK;
255}
256
257static CURLcode file_done(struct connectdata *conn,
258 CURLcode status, bool premature)
259{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700260 struct FILEPROTO *file = conn->data->req.protop;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100261 (void)status; /* not used */
262 (void)premature; /* not used */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100263
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700264 if(file) {
265 Curl_safefree(file->freepath);
266 file->path = NULL;
267 if(file->fd != -1)
268 close(file->fd);
269 file->fd = -1;
270 }
271
272 return CURLE_OK;
273}
274
275static CURLcode file_disconnect(struct connectdata *conn,
276 bool dead_connection)
277{
278 struct FILEPROTO *file = conn->data->req.protop;
279 (void)dead_connection; /* not used */
280
281 if(file) {
282 Curl_safefree(file->freepath);
283 file->path = NULL;
284 if(file->fd != -1)
285 close(file->fd);
286 file->fd = -1;
287 }
Kristian Monsen5ab50182010-05-14 18:53:44 +0100288
289 return CURLE_OK;
290}
291
292#ifdef DOS_FILESYSTEM
293#define DIRSEP '\\'
294#else
295#define DIRSEP '/'
296#endif
297
298static CURLcode file_upload(struct connectdata *conn)
299{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700300 struct FILEPROTO *file = conn->data->req.protop;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100301 const char *dir = strchr(file->path, DIRSEP);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700302 int fd;
303 int mode;
304 CURLcode result = CURLE_OK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100305 struct SessionHandle *data = conn->data;
306 char *buf = data->state.buffer;
307 size_t nread;
308 size_t nwrite;
309 curl_off_t bytecount = 0;
310 struct timeval now = Curl_tvnow();
311 struct_stat file_stat;
312 const char* buf2;
313
314 /*
315 * Since FILE: doesn't do the full init, we need to provide some extra
316 * assignments here.
317 */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100318 conn->data->req.upload_fromhere = buf;
319
320 if(!dir)
321 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
322
323 if(!dir[1])
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700324 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
325
326#ifdef O_BINARY
327#define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
328#else
329#define MODE_DEFAULT O_WRONLY|O_CREAT
330#endif
Kristian Monsen5ab50182010-05-14 18:53:44 +0100331
332 if(data->state.resume_from)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700333 mode = MODE_DEFAULT|O_APPEND;
334 else
335 mode = MODE_DEFAULT|O_TRUNC;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100336
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700337 fd = open(file->path, mode, conn->data->set.new_file_perms);
338 if(fd < 0) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100339 failf(data, "Can't open %s for writing", file->path);
340 return CURLE_WRITE_ERROR;
341 }
342
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700343 if(-1 != data->state.infilesize)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100344 /* known size of data to "upload" */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700345 Curl_pgrsSetUploadSize(data, data->state.infilesize);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100346
347 /* treat the negative resume offset value as the case of "-" */
348 if(data->state.resume_from < 0) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700349 if(fstat(fd, &file_stat)) {
350 close(fd);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100351 failf(data, "Can't get the size of %s", file->path);
352 return CURLE_WRITE_ERROR;
353 }
354 else
355 data->state.resume_from = (curl_off_t)file_stat.st_size;
356 }
357
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700358 while(!result) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100359 int readcount;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700360 result = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
361 if(result)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100362 break;
363
364 if(readcount <= 0) /* fix questionable compare error. curlvms */
365 break;
366
367 nread = (size_t)readcount;
368
369 /*skip bytes before resume point*/
370 if(data->state.resume_from) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700371 if((curl_off_t)nread <= data->state.resume_from ) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100372 data->state.resume_from -= nread;
373 nread = 0;
374 buf2 = buf;
375 }
376 else {
377 buf2 = buf + data->state.resume_from;
378 nread -= (size_t)data->state.resume_from;
379 data->state.resume_from = 0;
380 }
381 }
382 else
383 buf2 = buf;
384
385 /* write the data to the target */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700386 nwrite = write(fd, buf2, nread);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100387 if(nwrite != nread) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700388 result = CURLE_SEND_ERROR;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100389 break;
390 }
391
392 bytecount += nread;
393
394 Curl_pgrsSetUploadCounter(data, bytecount);
395
396 if(Curl_pgrsUpdate(conn))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700397 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100398 else
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700399 result = Curl_speedcheck(data, now);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100400 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700401 if(!result && Curl_pgrsUpdate(conn))
402 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100403
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700404 close(fd);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100405
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700406 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100407}
408
409/*
410 * file_do() is the protocol-specific function for the do-phase, separated
411 * from the connect-phase above. Other protocols merely setup the transfer in
412 * the do-phase, to have it done in the main transfer loop but since some
413 * platforms we support don't allow select()ing etc on file handles (as
414 * opposed to sockets) we instead perform the whole do-operation in this
415 * function.
416 */
417static CURLcode file_do(struct connectdata *conn, bool *done)
418{
419 /* This implementation ignores the host name in conformance with
420 RFC 1738. Only local files (reachable via the standard file system)
421 are supported. This means that files on remotely mounted directories
422 (via NFS, Samba, NT sharing) can be accessed through a file:// URL
423 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700424 CURLcode result = CURLE_OK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100425 struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
426 Windows version to have a different struct without
427 having to redefine the simple word 'stat' */
428 curl_off_t expected_size=0;
429 bool fstated=FALSE;
430 ssize_t nread;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100431 struct SessionHandle *data = conn->data;
432 char *buf = data->state.buffer;
433 curl_off_t bytecount = 0;
434 int fd;
435 struct timeval now = Curl_tvnow();
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700436 struct FILEPROTO *file;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100437
438 *done = TRUE; /* unconditionally */
439
440 Curl_initinfo(data);
441 Curl_pgrsStartNow(data);
442
443 if(data->set.upload)
444 return file_upload(conn);
445
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700446 file = conn->data->req.protop;
447
Kristian Monsen5ab50182010-05-14 18:53:44 +0100448 /* get the fd from the connection phase */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700449 fd = file->fd;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100450
451 /* VMS: This only works reliable for STREAMLF files */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700452 if(-1 != fstat(fd, &statbuf)) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100453 /* we could stat it, then read out the size */
454 expected_size = statbuf.st_size;
455 /* and store the modification time */
456 data->info.filetime = (long)statbuf.st_mtime;
457 fstated = TRUE;
458 }
459
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700460 if(fstated && !data->state.range && data->set.timecondition) {
461 if(!Curl_meets_timecondition(data, (time_t)data->info.filetime)) {
462 *done = TRUE;
463 return CURLE_OK;
464 }
465 }
466
Kristian Monsen5ab50182010-05-14 18:53:44 +0100467 /* If we have selected NOBODY and HEADER, it means that we only want file
468 information. Which for FILE can't be much more than the file size and
469 date. */
470 if(data->set.opt_no_body && data->set.include_header && fstated) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100471 snprintf(buf, sizeof(data->state.buffer),
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700472 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100473 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
474 if(result)
475 return result;
476
477 result = Curl_client_write(conn, CLIENTWRITE_BOTH,
478 (char *)"Accept-ranges: bytes\r\n", 0);
479 if(result)
480 return result;
481
482 if(fstated) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100483 time_t filetime = (time_t)statbuf.st_mtime;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100484 struct tm buffer;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700485 const struct tm *tm = &buffer;
486 result = Curl_gmtime(filetime, &buffer);
487 if(result)
488 return result;
489
Kristian Monsen5ab50182010-05-14 18:53:44 +0100490 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
491 snprintf(buf, BUFSIZE-1,
492 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
493 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
494 tm->tm_mday,
495 Curl_month[tm->tm_mon],
496 tm->tm_year + 1900,
497 tm->tm_hour,
498 tm->tm_min,
499 tm->tm_sec);
500 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
501 }
502 /* if we fstat()ed the file, set the file size to make it available post-
503 transfer */
504 if(fstated)
505 Curl_pgrsSetDownloadSize(data, expected_size);
506 return result;
507 }
508
509 /* Check whether file range has been specified */
510 file_range(conn);
511
512 /* Adjust the start offset in case we want to get the N last bytes
513 * of the stream iff the filesize could be determined */
514 if(data->state.resume_from < 0) {
515 if(!fstated) {
516 failf(data, "Can't get the size of file.");
517 return CURLE_READ_ERROR;
518 }
519 else
520 data->state.resume_from += (curl_off_t)statbuf.st_size;
521 }
522
523 if(data->state.resume_from <= expected_size)
524 expected_size -= data->state.resume_from;
525 else {
526 failf(data, "failed to resume file:// transfer");
527 return CURLE_BAD_DOWNLOAD_RESUME;
528 }
529
530 /* A high water mark has been specified so we obey... */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700531 if(data->req.maxdownload > 0)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100532 expected_size = data->req.maxdownload;
533
534 if(fstated && (expected_size == 0))
535 return CURLE_OK;
536
537 /* The following is a shortcut implementation of file reading
538 this is both more efficient than the former call to download() and
539 it avoids problems with select() and recv() on file descriptors
540 in Winsock */
541 if(fstated)
542 Curl_pgrsSetDownloadSize(data, expected_size);
543
544 if(data->state.resume_from) {
545 if(data->state.resume_from !=
546 lseek(fd, data->state.resume_from, SEEK_SET))
547 return CURLE_BAD_DOWNLOAD_RESUME;
548 }
549
550 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
551
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700552 while(!result) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100553 /* Don't fill a whole buffer if we want less than all data */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700554 size_t bytestoread =
555 (expected_size < CURL_OFF_T_C(BUFSIZE) - CURL_OFF_T_C(1)) ?
556 curlx_sotouz(expected_size) : BUFSIZE - 1;
557
Kristian Monsen5ab50182010-05-14 18:53:44 +0100558 nread = read(fd, buf, bytestoread);
559
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700560 if(nread > 0)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100561 buf[nread] = 0;
562
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700563 if(nread <= 0 || expected_size == 0)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100564 break;
565
566 bytecount += nread;
567 expected_size -= nread;
568
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700569 result = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
570 if(result)
571 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100572
573 Curl_pgrsSetDownloadCounter(data, bytecount);
574
575 if(Curl_pgrsUpdate(conn))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700576 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100577 else
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700578 result = Curl_speedcheck(data, now);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100579 }
580 if(Curl_pgrsUpdate(conn))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700581 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100582
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700583 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100584}
585
586#endif