1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2004, 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 -- */
33 #ifdef HAVE_SYS_TYPES_H
34 #include <sys/types.h>
36 #ifdef HAVE_SYS_STAT_H
42 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
47 #ifdef HAVE_SYS_SOCKET_H
48 #include <sys/socket.h>
50 #ifdef HAVE_NETINET_IN_H
51 #include <netinet/in.h>
60 #ifdef HAVE_ARPA_INET_H
61 #include <arpa/inet.h>
66 #include <sys/ioctl.h>
69 #ifdef HAVE_SYS_PARAM_H
70 #include <sys/param.h>
80 #include <curl/curl.h>
85 #include "speedcheck.h"
91 #define _MPRINTF_REPLACE /* use our functions only */
92 #include <curl/mprintf.h>
94 /* The last #include file should be: */
98 * Curl_file_connect() gets called from Curl_protocol_connect() to allow us to
99 * do protocol-specific actions at connect-time. We emulate a
100 * connect-then-transfer protocol and "connect" to the file here
102 CURLcode Curl_file_connect(struct connectdata *conn)
104 char *real_path = curl_unescape(conn->path, 0);
105 struct FILEPROTO *file;
107 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
113 return CURLE_OUT_OF_MEMORY;
115 file = (struct FILEPROTO *)calloc(sizeof(struct FILEPROTO), 1);
118 return CURLE_OUT_OF_MEMORY;
121 conn->proto.file = file;
123 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
124 /* If the first character is a slash, and there's
125 something that looks like a drive at the beginning of
126 the path, skip the slash. If we remove the initial
127 slash in all cases, paths without drive letters end up
128 relative to the current directory which isn't how
131 Some browsers accept | instead of : as the drive letter
132 separator, so we do too.
134 On other platforms, we need the slash to indicate an
135 absolute pathname. On Windows, absolute paths start
138 actual_path = real_path;
139 if ((actual_path[0] == '/') &&
141 (actual_path[2] == ':' || actual_path[2] == '|'))
143 actual_path[2] = ':';
147 /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
148 for (i=0; actual_path[i] != '\0'; ++i)
149 if (actual_path[i] == '/')
150 actual_path[i] = '\\';
152 fd = open(actual_path, O_RDONLY | O_BINARY); /* no CR/LF translation! */
153 file->path = actual_path;
155 fd = open(real_path, O_RDONLY);
156 file->path = real_path;
158 file->freepath = real_path; /* free this when done */
160 if(!conn->data->set.upload && (fd == -1)) {
161 failf(conn->data, "Couldn't open file %s", conn->path);
162 Curl_file_done(conn, CURLE_FILE_COULDNT_READ_FILE);
163 return CURLE_FILE_COULDNT_READ_FILE;
170 #if defined(WIN32) && (SIZEOF_CURL_OFF_T > 4)
171 #define lseek(x,y,z) _lseeki64(x, y, z)
174 CURLcode Curl_file_done(struct connectdata *conn,
177 struct FILEPROTO *file = conn->proto.file;
178 (void)status; /* not used */
179 Curl_safefree(file->freepath);
187 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
193 static CURLcode file_upload(struct connectdata *conn)
195 struct FILEPROTO *file = conn->proto.file;
196 char *dir = strchr(file->path, DIRSEP);
198 CURLcode res=CURLE_OK;
199 struct SessionHandle *data = conn->data;
200 char *buf = data->state.buffer;
203 curl_off_t bytecount = 0;
204 struct timeval now = Curl_tvnow();
207 * Since FILE: doesn't do the full init, we need to provide some extra
210 conn->fread = data->set.fread;
211 conn->fread_in = data->set.in;
212 conn->upload_fromhere = buf;
215 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
218 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
220 fp = fopen(file->path, "wb");
222 failf(data, "Can't open %s for writing", file->path);
223 return CURLE_WRITE_ERROR;
226 if(-1 != data->set.infilesize)
227 /* known size of data to "upload" */
228 Curl_pgrsSetUploadSize(data, data->set.infilesize);
230 while (res == CURLE_OK) {
232 res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
236 nread = (size_t)readcount;
241 /* write the data to the target */
242 nwrite = fwrite(buf, 1, nread, fp);
243 if(nwrite != nread) {
244 res = CURLE_SEND_ERROR;
250 Curl_pgrsSetUploadCounter(data, bytecount);
252 if(Curl_pgrsUpdate(conn))
253 res = CURLE_ABORTED_BY_CALLBACK;
255 res = Curl_speedcheck(data, now);
257 if(!res && Curl_pgrsUpdate(conn))
258 res = CURLE_ABORTED_BY_CALLBACK;
266 * Curl_file() is the protocol-specific function for the do-phase, separated
267 * from the connect-phase above. Other protocols merely setup the transfer in
268 * the do-phase, to have it done in the main transfer loop but since some
269 * platforms we support don't allow select()ing etc on file handles (as
270 * opposed to sockets) we instead perform the whole do-operation in this
273 CURLcode Curl_file(struct connectdata *conn)
275 /* This implementation ignores the host name in conformance with
276 RFC 1738. Only local files (reachable via the standard file system)
277 are supported. This means that files on remotely mounted directories
278 (via NFS, Samba, NT sharing) can be accessed through a file:// URL
280 CURLcode res = CURLE_OK;
282 curl_off_t expected_size=0;
285 struct SessionHandle *data = conn->data;
286 char *buf = data->state.buffer;
287 curl_off_t bytecount = 0;
289 struct timeval now = Curl_tvnow();
291 Curl_readwrite_init(conn);
293 Curl_pgrsStartNow(data);
296 return file_upload(conn);
298 /* get the fd from the connection phase */
299 fd = conn->proto.file->fd;
301 /* VMS: This only works reliable for STREAMLF files */
302 if( -1 != fstat(fd, &statbuf)) {
303 /* we could stat it, then read out the size */
304 expected_size = statbuf.st_size;
308 /* If we have selected NOBODY and HEADER, it means that we only want file
309 information. Which for FILE can't be much more than the file size and
311 if(conn->bits.no_body && data->set.include_header && fstated) {
313 snprintf(buf, sizeof(data->state.buffer),
314 "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
315 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
319 result = Curl_client_write(data, CLIENTWRITE_BOTH,
320 (char *)"Accept-ranges: bytes\r\n", 0);
327 time_t clock = (time_t)statbuf.st_mtime;
330 tm = (struct tm *)gmtime_r(&clock, &buffer);
334 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
335 strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n",
337 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
343 if (conn->resume_from <= expected_size)
344 expected_size -= conn->resume_from;
346 failf(data, "failed to resume file:// transfer");
347 return CURLE_BAD_DOWNLOAD_RESUME;
350 if (fstated && (expected_size == 0))
353 /* The following is a shortcut implementation of file reading
354 this is both more efficient than the former call to download() and
355 it avoids problems with select() and recv() on file descriptors
358 Curl_pgrsSetDownloadSize(data, expected_size);
360 if(conn->resume_from)
361 lseek(fd, conn->resume_from, SEEK_SET);
363 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
365 while (res == CURLE_OK) {
366 nread = read(fd, buf, BUFSIZE-1);
376 res = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread);
380 Curl_pgrsSetDownloadCounter(data, bytecount);
382 if(Curl_pgrsUpdate(conn))
383 res = CURLE_ABORTED_BY_CALLBACK;
385 res = Curl_speedcheck(data, now);
387 if(Curl_pgrsUpdate(conn))
388 res = CURLE_ABORTED_BY_CALLBACK;