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 #include <sys/types.h>
38 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
46 #ifdef HAVE_NETINET_IN_H
47 #include <netinet/in.h>
56 #ifdef HAVE_ARPA_INET_H
57 #include <arpa/inet.h>
62 #include <sys/ioctl.h>
65 #ifdef HAVE_SYS_PARAM_H
66 #include <sys/param.h>
69 #ifdef HAVE_SYS_STAT_H
79 #include <curl/curl.h>
84 #include "speedcheck.h"
90 #define _MPRINTF_REPLACE /* use our functions only */
91 #include <curl/mprintf.h>
93 /* The last #include file should be: */
97 * Curl_file_connect() gets called from Curl_protocol_connect() to allow us to
98 * do protocol-specific actions at connect-time. We emulate a
99 * connect-then-transfer protocol and "connect" to the file here
101 CURLcode Curl_file_connect(struct connectdata *conn)
103 char *real_path = curl_unescape(conn->path, 0);
104 struct FILEPROTO *file;
106 #if defined(WIN32) || defined(__EMX__)
112 return CURLE_OUT_OF_MEMORY;
114 file = (struct FILEPROTO *)calloc(sizeof(struct FILEPROTO), 1);
117 return CURLE_OUT_OF_MEMORY;
120 conn->proto.file = file;
122 #if defined(WIN32) || defined(__EMX__)
123 /* If the first character is a slash, and there's
124 something that looks like a drive at the beginning of
125 the path, skip the slash. If we remove the initial
126 slash in all cases, paths without drive letters end up
127 relative to the current directory which isn't how
130 Some browsers accept | instead of : as the drive letter
131 separator, so we do too.
133 On other platforms, we need the slash to indicate an
134 absolute pathname. On Windows, absolute paths start
137 actual_path = real_path;
138 if ((actual_path[0] == '/') &&
140 (actual_path[2] == ':' || actual_path[2] == '|'))
142 actual_path[2] = ':';
146 /* change path separators from '/' to '\\' for Windows and OS/2 */
147 for (i=0; actual_path[i] != '\0'; ++i)
148 if (actual_path[i] == '/')
149 actual_path[i] = '\\';
151 fd = open(actual_path, O_RDONLY | O_BINARY); /* no CR/LF translation! */
152 file->path = actual_path;
154 fd = open(real_path, O_RDONLY);
155 file->path = real_path;
157 file->freepath = real_path; /* free this when done */
159 if(!conn->data->set.upload && (fd == -1)) {
160 failf(conn->data, "Couldn't open file %s", conn->path);
161 Curl_file_done(conn, CURLE_FILE_COULDNT_READ_FILE);
162 return CURLE_FILE_COULDNT_READ_FILE;
169 #if defined(WIN32) && (SIZEOF_CURL_OFF_T > 4)
170 #define lseek(x,y,z) _lseeki64(x, y, z)
173 CURLcode Curl_file_done(struct connectdata *conn,
176 struct FILEPROTO *file = conn->proto.file;
177 (void)status; /* not used */
178 Curl_safefree(file->freepath);
183 #if defined(WIN32) || defined(__EMX__)
189 static CURLcode file_upload(struct connectdata *conn)
191 struct FILEPROTO *file = conn->proto.file;
192 char *dir = strchr(file->path, DIRSEP);
194 CURLcode res=CURLE_OK;
195 struct SessionHandle *data = conn->data;
196 char *buf = data->state.buffer;
199 curl_off_t bytecount = 0;
200 struct timeval now = Curl_tvnow();
203 * Since FILE: doesn't do the full init, we need to provide some extra
206 conn->fread = data->set.fread;
207 conn->fread_in = data->set.in;
208 conn->upload_fromhere = buf;
211 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
214 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
216 fp = fopen(file->path, "wb");
218 failf(data, "Can't open %s for writing", file->path);
219 return CURLE_WRITE_ERROR;
222 if(-1 != data->set.infilesize)
223 /* known size of data to "upload" */
224 Curl_pgrsSetUploadSize(data, data->set.infilesize);
226 while (res == CURLE_OK) {
228 res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
232 nread = (size_t)readcount;
237 /* write the data to the target */
238 nwrite = fwrite(buf, 1, nread, fp);
239 if(nwrite != nread) {
240 res = CURLE_SEND_ERROR;
246 Curl_pgrsSetUploadCounter(data, bytecount);
248 if(Curl_pgrsUpdate(conn))
249 res = CURLE_ABORTED_BY_CALLBACK;
251 res = Curl_speedcheck(data, now);
253 if(!res && Curl_pgrsUpdate(conn))
254 res = CURLE_ABORTED_BY_CALLBACK;
262 * Curl_file() is the protocol-specific function for the do-phase, separated
263 * from the connect-phase above. Other protocols merely setup the transfer in
264 * the do-phase, to have it done in the main transfer loop but since some
265 * platforms we support don't allow select()ing etc on file handles (as
266 * opposed to sockets) we instead perform the whole do-operation in this
269 CURLcode Curl_file(struct connectdata *conn)
271 /* This implementation ignores the host name in conformance with
272 RFC 1738. Only local files (reachable via the standard file system)
273 are supported. This means that files on remotely mounted directories
274 (via NFS, Samba, NT sharing) can be accessed through a file:// URL
276 CURLcode res = CURLE_OK;
278 curl_off_t expected_size=0;
281 struct SessionHandle *data = conn->data;
282 char *buf = data->state.buffer;
283 curl_off_t bytecount = 0;
285 struct timeval now = Curl_tvnow();
287 Curl_readwrite_init(conn);
289 Curl_pgrsStartNow(data);
292 return file_upload(conn);
294 /* get the fd from the connection phase */
295 fd = conn->proto.file->fd;
297 /* VMS: This only works reliable for STREAMLF files */
298 if( -1 != fstat(fd, &statbuf)) {
299 /* we could stat it, then read out the size */
300 expected_size = statbuf.st_size;
304 /* If we have selected NOBODY and HEADER, it means that we only want file
305 information. Which for FILE can't be much more than the file size and
307 if(conn->bits.no_body && data->set.include_header && fstated) {
309 snprintf(buf, sizeof(data->state.buffer),
310 "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
311 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
315 result = Curl_client_write(data, CLIENTWRITE_BOTH,
316 (char *)"Accept-ranges: bytes\r\n", 0);
323 time_t clock = (time_t)statbuf.st_mtime;
326 tm = (struct tm *)gmtime_r(&clock, &buffer);
330 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
331 strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n",
333 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
339 /* Added by Dolbneff A.V & Spiridonoff A.V */
340 if (conn->resume_from <= expected_size)
341 expected_size -= conn->resume_from;
343 /* Is this error code suitable in such situation? */
344 return CURLE_FTP_BAD_DOWNLOAD_RESUME;
346 if (fstated && (expected_size == 0))
349 /* The following is a shortcut implementation of file reading
350 this is both more efficient than the former call to download() and
351 it avoids problems with select() and recv() on file descriptors
354 Curl_pgrsSetDownloadSize(data, expected_size);
356 if(conn->resume_from)
357 lseek(fd, conn->resume_from, SEEK_SET);
359 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
361 while (res == CURLE_OK) {
362 nread = read(fd, buf, BUFSIZE-1);
372 res = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread);
376 Curl_pgrsSetDownloadCounter(data, bytecount);
378 if(Curl_pgrsUpdate(conn))
379 res = CURLE_ABORTED_BY_CALLBACK;
381 res = Curl_speedcheck(data, now);
383 if(Curl_pgrsUpdate(conn))
384 res = CURLE_ABORTED_BY_CALLBACK;