1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
22 ***************************************************************************/
26 #ifndef CURL_DISABLE_FILE
27 /* -- WIN32 approved -- */
39 #ifdef HAVE_SYS_SOCKET_H
40 #include <sys/socket.h>
42 #ifdef HAVE_NETINET_IN_H
43 #include <netinet/in.h>
45 #ifdef HAVE_SYS_TIME_H
54 #ifdef HAVE_ARPA_INET_H
55 #include <arpa/inet.h>
60 #include <sys/ioctl.h>
63 #ifdef HAVE_SYS_PARAM_H
64 #include <sys/param.h>
73 #include "strtoofft.h"
75 #include <curl/curl.h>
80 #include "speedcheck.h"
85 #include "parsedate.h" /* for the week day and month names */
87 #define _MPRINTF_REPLACE /* use our functions only */
88 #include <curl/mprintf.h>
90 /* The last #include file should be: */
95 * Forward declarations.
98 static CURLcode file_do(struct connectdata *, bool *done);
99 static CURLcode file_done(struct connectdata *conn,
100 CURLcode status, bool premature);
101 static CURLcode file_connect(struct connectdata *conn, bool *done);
104 * FILE scheme handler.
107 const struct Curl_handler Curl_handler_file = {
109 ZERO_NULL, /* setup_connection */
111 file_done, /* done */
112 ZERO_NULL, /* do_more */
113 file_connect, /* connect_it */
114 ZERO_NULL, /* connecting */
115 ZERO_NULL, /* doing */
116 ZERO_NULL, /* proto_getsock */
117 ZERO_NULL, /* doing_getsock */
118 ZERO_NULL, /* disconnect */
120 PROT_FILE /* protocol */
125 Check if this is a range download, and if so, set the internal variables
126 properly. This code is copied from the FTP implementation and might as
127 well be factored out.
129 static CURLcode file_range(struct connectdata *conn)
132 curl_off_t totalsize=-1;
135 struct SessionHandle *data = conn->data;
137 if(data->state.use_range && data->state.range) {
138 from=curlx_strtoofft(data->state.range, &ptr, 0);
139 while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
141 to=curlx_strtoofft(ptr, &ptr2, 0);
143 /* we didn't get any digit */
146 if((-1 == to) && (from>=0)) {
148 data->state.resume_from = from;
149 DEBUGF(infof(data, "RANGE %" FORMAT_OFF_T " to end of file\n",
155 data->req.maxdownload = -from;
156 data->state.resume_from = from;
157 DEBUGF(infof(data, "RANGE the last %" FORMAT_OFF_T " bytes\n",
163 data->req.maxdownload = totalsize+1; /* include last byte */
164 data->state.resume_from = from;
165 DEBUGF(infof(data, "RANGE from %" FORMAT_OFF_T
166 " getting %" FORMAT_OFF_T " bytes\n",
167 from, data->req.maxdownload));
169 DEBUGF(infof(data, "range-download from %" FORMAT_OFF_T
170 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
171 from, to, data->req.maxdownload));
174 data->req.maxdownload = -1;
179 * file_connect() gets called from Curl_protocol_connect() to allow us to
180 * do protocol-specific actions at connect-time. We emulate a
181 * connect-then-transfer protocol and "connect" to the file here
183 static CURLcode file_connect(struct connectdata *conn, bool *done)
185 struct SessionHandle *data = conn->data;
186 char *real_path = curl_easy_unescape(data, data->state.path, 0, NULL);
187 struct FILEPROTO *file;
189 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
195 return CURLE_OUT_OF_MEMORY;
197 /* If there already is a protocol-specific struct allocated for this
198 sessionhandle, deal with it */
199 Curl_reset_reqproto(conn);
201 if(!data->state.proto.file) {
202 file = (struct FILEPROTO *)calloc(sizeof(struct FILEPROTO), 1);
205 return CURLE_OUT_OF_MEMORY;
207 data->state.proto.file = file;
210 /* file is not a protocol that can deal with "persistancy" */
211 file = data->state.proto.file;
212 Curl_safefree(file->freepath);
216 file->freepath = NULL;
220 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
221 /* If the first character is a slash, and there's
222 something that looks like a drive at the beginning of
223 the path, skip the slash. If we remove the initial
224 slash in all cases, paths without drive letters end up
225 relative to the current directory which isn't how
228 Some browsers accept | instead of : as the drive letter
229 separator, so we do too.
231 On other platforms, we need the slash to indicate an
232 absolute pathname. On Windows, absolute paths start
235 actual_path = real_path;
236 if((actual_path[0] == '/') &&
238 (actual_path[2] == ':' || actual_path[2] == '|'))
240 actual_path[2] = ':';
244 /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
245 for (i=0; actual_path[i] != '\0'; ++i)
246 if(actual_path[i] == '/')
247 actual_path[i] = '\\';
249 fd = open(actual_path, O_RDONLY | O_BINARY); /* no CR/LF translation! */
250 file->path = actual_path;
252 fd = open(real_path, O_RDONLY);
253 file->path = real_path;
255 file->freepath = real_path; /* free this when done */
258 if(!data->set.upload && (fd == -1)) {
259 failf(data, "Couldn't open file %s", data->state.path);
260 file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
261 return CURLE_FILE_COULDNT_READ_FILE;
268 static CURLcode file_done(struct connectdata *conn,
269 CURLcode status, bool premature)
271 struct FILEPROTO *file = conn->data->state.proto.file;
272 (void)status; /* not used */
273 (void)premature; /* not used */
274 Curl_safefree(file->freepath);
282 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
288 static CURLcode file_upload(struct connectdata *conn)
290 struct FILEPROTO *file = conn->data->state.proto.file;
291 const char *dir = strchr(file->path, DIRSEP);
293 CURLcode res=CURLE_OK;
294 struct SessionHandle *data = conn->data;
295 char *buf = data->state.buffer;
298 curl_off_t bytecount = 0;
299 struct timeval now = Curl_tvnow();
300 struct_stat file_stat;
304 * Since FILE: doesn't do the full init, we need to provide some extra
307 conn->fread_func = data->set.fread_func;
308 conn->fread_in = data->set.in;
309 conn->data->req.upload_fromhere = buf;
312 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
315 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
317 if(data->state.resume_from)
318 fp = fopen( file->path, "ab" );
322 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
323 fd = open(file->path, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,
324 conn->data->set.new_file_perms);
325 #else /* !(WIN32 || MSDOS || __EMX__) */
326 fd = open(file->path, O_WRONLY|O_CREAT|O_TRUNC,
327 conn->data->set.new_file_perms);
328 #endif /* !(WIN32 || MSDOS || __EMX__) */
330 failf(data, "Can't open %s for writing", file->path);
331 return CURLE_WRITE_ERROR;
333 fp = fdopen(fd, "wb");
337 failf(data, "Can't open %s for writing", file->path);
338 return CURLE_WRITE_ERROR;
341 if(-1 != data->set.infilesize)
342 /* known size of data to "upload" */
343 Curl_pgrsSetUploadSize(data, data->set.infilesize);
345 /* treat the negative resume offset value as the case of "-" */
346 if(data->state.resume_from < 0) {
347 if(stat(file->path, &file_stat)) {
349 failf(data, "Can't get the size of %s", file->path);
350 return CURLE_WRITE_ERROR;
353 data->state.resume_from = (curl_off_t)file_stat.st_size;
356 while(res == CURLE_OK) {
358 res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
362 if(readcount <= 0) /* fix questionable compare error. curlvms */
365 nread = (size_t)readcount;
367 /*skip bytes before resume point*/
368 if(data->state.resume_from) {
369 if( (curl_off_t)nread <= data->state.resume_from ) {
370 data->state.resume_from -= nread;
375 buf2 = buf + data->state.resume_from;
376 nread -= (size_t)data->state.resume_from;
377 data->state.resume_from = 0;
383 /* write the data to the target */
384 nwrite = fwrite(buf2, 1, nread, fp);
385 if(nwrite != nread) {
386 res = CURLE_SEND_ERROR;
392 Curl_pgrsSetUploadCounter(data, bytecount);
394 if(Curl_pgrsUpdate(conn))
395 res = CURLE_ABORTED_BY_CALLBACK;
397 res = Curl_speedcheck(data, now);
399 if(!res && Curl_pgrsUpdate(conn))
400 res = CURLE_ABORTED_BY_CALLBACK;
408 * file_do() is the protocol-specific function for the do-phase, separated
409 * from the connect-phase above. Other protocols merely setup the transfer in
410 * the do-phase, to have it done in the main transfer loop but since some
411 * platforms we support don't allow select()ing etc on file handles (as
412 * opposed to sockets) we instead perform the whole do-operation in this
415 static CURLcode file_do(struct connectdata *conn, bool *done)
417 /* This implementation ignores the host name in conformance with
418 RFC 1738. Only local files (reachable via the standard file system)
419 are supported. This means that files on remotely mounted directories
420 (via NFS, Samba, NT sharing) can be accessed through a file:// URL
422 CURLcode res = CURLE_OK;
423 struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
424 Windows version to have a different struct without
425 having to redefine the simple word 'stat' */
426 curl_off_t expected_size=0;
430 struct SessionHandle *data = conn->data;
431 char *buf = data->state.buffer;
432 curl_off_t bytecount = 0;
434 struct timeval now = Curl_tvnow();
436 *done = TRUE; /* unconditionally */
439 Curl_pgrsStartNow(data);
442 return file_upload(conn);
444 /* get the fd from the connection phase */
445 fd = conn->data->state.proto.file->fd;
447 /* VMS: This only works reliable for STREAMLF files */
448 if( -1 != fstat(fd, &statbuf)) {
449 /* we could stat it, then read out the size */
450 expected_size = statbuf.st_size;
454 /* If we have selected NOBODY and HEADER, it means that we only want file
455 information. Which for FILE can't be much more than the file size and
457 if(conn->bits.no_body && data->set.include_header && fstated) {
459 snprintf(buf, sizeof(data->state.buffer),
460 "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
461 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
465 result = Curl_client_write(conn, CLIENTWRITE_BOTH,
466 (char *)"Accept-ranges: bytes\r\n", 0);
472 time_t filetime = (time_t)statbuf.st_mtime;
475 tm = (const struct tm *)gmtime_r(&filetime, &buffer);
477 tm = gmtime(&filetime);
479 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
480 snprintf(buf, BUFSIZE-1,
481 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
482 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
484 Curl_month[tm->tm_mon],
489 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
494 /* Check whether file range has been specified */
497 /* Adjust the start offset in case we want to get the N last bytes
498 * of the stream iff the filesize could be determined */
499 if(data->state.resume_from < 0) {
501 failf(data, "Can't get the size of file.");
502 return CURLE_READ_ERROR;
505 data->state.resume_from += (curl_off_t)statbuf.st_size;
508 if(data->state.resume_from <= expected_size)
509 expected_size -= data->state.resume_from;
511 failf(data, "failed to resume file:// transfer");
512 return CURLE_BAD_DOWNLOAD_RESUME;
515 /* A high water mark has been specified so we obey... */
516 if (data->req.maxdownload > 0)
517 expected_size = data->req.maxdownload;
519 if(fstated && (expected_size == 0))
522 /* The following is a shortcut implementation of file reading
523 this is both more efficient than the former call to download() and
524 it avoids problems with select() and recv() on file descriptors
527 Curl_pgrsSetDownloadSize(data, expected_size);
529 if(data->state.resume_from) {
530 if(data->state.resume_from !=
531 lseek(fd, data->state.resume_from, SEEK_SET))
532 return CURLE_BAD_DOWNLOAD_RESUME;
535 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
537 while(res == CURLE_OK) {
538 /* Don't fill a whole buffer if we want less than all data */
539 bytestoread = (expected_size < BUFSIZE-1)?(size_t)expected_size:BUFSIZE-1;
540 nread = read(fd, buf, bytestoread);
545 if (nread <= 0 || expected_size == 0)
549 expected_size -= nread;
551 res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
555 Curl_pgrsSetDownloadCounter(data, bytecount);
557 if(Curl_pgrsUpdate(conn))
558 res = CURLE_ABORTED_BY_CALLBACK;
560 res = Curl_speedcheck(data, now);
562 if(Curl_pgrsUpdate(conn))
563 res = CURLE_ABORTED_BY_CALLBACK;