1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2006, 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
40 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
45 #ifdef HAVE_SYS_SOCKET_H
46 #include <sys/socket.h>
48 #ifdef HAVE_NETINET_IN_H
49 #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"
90 #include "parsedate.h" /* for the week day and month names */
92 #define _MPRINTF_REPLACE /* use our functions only */
93 #include <curl/mprintf.h>
95 /* The last #include file should be: */
99 * Curl_file_connect() gets called from Curl_protocol_connect() to allow us to
100 * do protocol-specific actions at connect-time. We emulate a
101 * connect-then-transfer protocol and "connect" to the file here
103 CURLcode Curl_file_connect(struct connectdata *conn)
105 char *real_path = curl_easy_unescape(conn->data, conn->path, 0, NULL);
106 struct FILEPROTO *file;
108 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
114 return CURLE_OUT_OF_MEMORY;
116 file = (struct FILEPROTO *)calloc(sizeof(struct FILEPROTO), 1);
119 return CURLE_OUT_OF_MEMORY;
122 conn->proto.file = file;
124 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
125 /* If the first character is a slash, and there's
126 something that looks like a drive at the beginning of
127 the path, skip the slash. If we remove the initial
128 slash in all cases, paths without drive letters end up
129 relative to the current directory which isn't how
132 Some browsers accept | instead of : as the drive letter
133 separator, so we do too.
135 On other platforms, we need the slash to indicate an
136 absolute pathname. On Windows, absolute paths start
139 actual_path = real_path;
140 if ((actual_path[0] == '/') &&
142 (actual_path[2] == ':' || actual_path[2] == '|'))
144 actual_path[2] = ':';
148 /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
149 for (i=0; actual_path[i] != '\0'; ++i)
150 if (actual_path[i] == '/')
151 actual_path[i] = '\\';
153 fd = open(actual_path, O_RDONLY | O_BINARY); /* no CR/LF translation! */
154 file->path = actual_path;
156 fd = open(real_path, O_RDONLY);
157 file->path = real_path;
159 file->freepath = real_path; /* free this when done */
162 if(!conn->data->set.upload && (fd == -1)) {
163 failf(conn->data, "Couldn't open file %s", conn->path);
164 Curl_file_done(conn, CURLE_FILE_COULDNT_READ_FILE);
165 return CURLE_FILE_COULDNT_READ_FILE;
171 CURLcode Curl_file_done(struct connectdata *conn,
174 struct FILEPROTO *file = conn->proto.file;
175 (void)status; /* not used */
176 Curl_safefree(file->freepath);
184 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
190 static CURLcode file_upload(struct connectdata *conn)
192 struct FILEPROTO *file = conn->proto.file;
193 char *dir = strchr(file->path, DIRSEP);
195 CURLcode res=CURLE_OK;
196 struct SessionHandle *data = conn->data;
197 char *buf = data->state.buffer;
200 curl_off_t bytecount = 0;
201 struct timeval now = Curl_tvnow();
204 * Since FILE: doesn't do the full init, we need to provide some extra
207 conn->fread = data->set.fread;
208 conn->fread_in = data->set.in;
209 conn->upload_fromhere = buf;
212 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
215 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
217 fp = fopen(file->path, "wb");
219 failf(data, "Can't open %s for writing", file->path);
220 return CURLE_WRITE_ERROR;
223 if(-1 != data->set.infilesize)
224 /* known size of data to "upload" */
225 Curl_pgrsSetUploadSize(data, data->set.infilesize);
227 while (res == CURLE_OK) {
229 res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
233 if (readcount <= 0) /* fix questionable compare error. curlvms */
236 nread = (size_t)readcount;
238 /* write the data to the target */
239 nwrite = fwrite(buf, 1, nread, fp);
240 if(nwrite != nread) {
241 res = CURLE_SEND_ERROR;
247 Curl_pgrsSetUploadCounter(data, bytecount);
249 if(Curl_pgrsUpdate(conn))
250 res = CURLE_ABORTED_BY_CALLBACK;
252 res = Curl_speedcheck(data, now);
254 if(!res && Curl_pgrsUpdate(conn))
255 res = CURLE_ABORTED_BY_CALLBACK;
263 * Curl_file() is the protocol-specific function for the do-phase, separated
264 * from the connect-phase above. Other protocols merely setup the transfer in
265 * the do-phase, to have it done in the main transfer loop but since some
266 * platforms we support don't allow select()ing etc on file handles (as
267 * opposed to sockets) we instead perform the whole do-operation in this
270 CURLcode Curl_file(struct connectdata *conn, bool *done)
272 /* This implementation ignores the host name in conformance with
273 RFC 1738. Only local files (reachable via the standard file system)
274 are supported. This means that files on remotely mounted directories
275 (via NFS, Samba, NT sharing) can be accessed through a file:// URL
277 CURLcode res = CURLE_OK;
278 struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
279 Windows version to have a different struct without
280 having to redefine the simple word 'stat' */
281 curl_off_t expected_size=0;
284 struct SessionHandle *data = conn->data;
285 char *buf = data->state.buffer;
286 curl_off_t bytecount = 0;
288 struct timeval now = Curl_tvnow();
290 *done = TRUE; /* unconditionally */
292 Curl_readwrite_init(conn);
294 Curl_pgrsStartNow(data);
297 return file_upload(conn);
299 /* get the fd from the connection phase */
300 fd = conn->proto.file->fd;
302 /* VMS: This only works reliable for STREAMLF files */
303 if( -1 != fstat(fd, &statbuf)) {
304 /* we could stat it, then read out the size */
305 expected_size = statbuf.st_size;
309 /* If we have selected NOBODY and HEADER, it means that we only want file
310 information. Which for FILE can't be much more than the file size and
312 if(conn->bits.no_body && data->set.include_header && fstated) {
314 snprintf(buf, sizeof(data->state.buffer),
315 "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
316 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
320 result = Curl_client_write(conn, CLIENTWRITE_BOTH,
321 (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 snprintf(buf, BUFSIZE-1,
336 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
337 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
339 Curl_month[tm->tm_mon],
344 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
349 if (conn->resume_from <= expected_size)
350 expected_size -= conn->resume_from;
352 failf(data, "failed to resume file:// transfer");
353 return CURLE_BAD_DOWNLOAD_RESUME;
356 if (fstated && (expected_size == 0))
359 /* The following is a shortcut implementation of file reading
360 this is both more efficient than the former call to download() and
361 it avoids problems with select() and recv() on file descriptors
364 Curl_pgrsSetDownloadSize(data, expected_size);
366 if(conn->resume_from)
367 lseek(fd, conn->resume_from, SEEK_SET);
369 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
371 while (res == CURLE_OK) {
372 nread = read(fd, buf, BUFSIZE-1);
382 res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
386 Curl_pgrsSetDownloadCounter(data, bytecount);
388 if(Curl_pgrsUpdate(conn))
389 res = CURLE_ABORTED_BY_CALLBACK;
391 res = Curl_speedcheck(data, now);
393 if(Curl_pgrsUpdate(conn))
394 res = CURLE_ABORTED_BY_CALLBACK;