038bf42e17b37785b9e5417cec6901623970a0b4
[platform/upstream/curl.git] / lib / file.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
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
23 #include "curl_setup.h"
24
25 #ifndef CURL_DISABLE_FILE
26
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
29 #endif
30 #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
51 #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"
62 #include "curl_memory.h"
63 #include "parsedate.h" /* for the week day and month names */
64 #include "warnless.h"
65
66 #define _MPRINTF_REPLACE /* use our functions only */
67 #include <curl/mprintf.h>
68
69 /* The last #include file should be: */
70 #include "memdebug.h"
71
72 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \
73   defined(__SYMBIAN32__)
74 #define DOS_FILESYSTEM 1
75 #endif
76
77 #ifdef OPEN_NEEDS_ARG3
78 #  define open_readonly(p,f) open((p),(f),(0))
79 #else
80 #  define open_readonly(p,f) open((p),(f))
81 #endif
82
83 /*
84  * Forward declarations.
85  */
86
87 static CURLcode file_do(struct connectdata *, bool *done);
88 static CURLcode file_done(struct connectdata *conn,
89                           CURLcode status, bool premature);
90 static CURLcode file_connect(struct connectdata *conn, bool *done);
91 static CURLcode file_disconnect(struct connectdata *conn,
92                                 bool dead_connection);
93
94
95 /*
96  * FILE scheme handler.
97  */
98
99 const struct Curl_handler Curl_handler_file = {
100   "FILE",                               /* scheme */
101   ZERO_NULL,                            /* setup_connection */
102   file_do,                              /* do_it */
103   file_done,                            /* done */
104   ZERO_NULL,                            /* do_more */
105   file_connect,                         /* connect_it */
106   ZERO_NULL,                            /* connecting */
107   ZERO_NULL,                            /* doing */
108   ZERO_NULL,                            /* proto_getsock */
109   ZERO_NULL,                            /* doing_getsock */
110   ZERO_NULL,                            /* domore_getsock */
111   ZERO_NULL,                            /* perform_getsock */
112   file_disconnect,                      /* disconnect */
113   ZERO_NULL,                            /* readwrite */
114   0,                                    /* defport */
115   CURLPROTO_FILE,                       /* protocol */
116   PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
117 };
118
119
120  /*
121   Check if this is a range download, and if so, set the internal variables
122   properly. This code is copied from the FTP implementation and might as
123   well be factored out.
124  */
125 static CURLcode file_range(struct connectdata *conn)
126 {
127   curl_off_t from, to;
128   curl_off_t totalsize=-1;
129   char *ptr;
130   char *ptr2;
131   struct SessionHandle *data = conn->data;
132
133   if(data->state.use_range && data->state.range) {
134     from=curlx_strtoofft(data->state.range, &ptr, 0);
135     while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
136       ptr++;
137     to=curlx_strtoofft(ptr, &ptr2, 0);
138     if(ptr == ptr2) {
139       /* we didn't get any digit */
140       to=-1;
141     }
142     if((-1 == to) && (from>=0)) {
143       /* X - */
144       data->state.resume_from = from;
145       DEBUGF(infof(data, "RANGE %" FORMAT_OFF_T " to end of file\n",
146                    from));
147     }
148     else if(from < 0) {
149       /* -Y */
150       data->req.maxdownload = -from;
151       data->state.resume_from = from;
152       DEBUGF(infof(data, "RANGE the last %" FORMAT_OFF_T " bytes\n",
153                    -from));
154     }
155     else {
156       /* X-Y */
157       totalsize = to-from;
158       data->req.maxdownload = totalsize+1; /* include last byte */
159       data->state.resume_from = from;
160       DEBUGF(infof(data, "RANGE from %" FORMAT_OFF_T
161                    " getting %" FORMAT_OFF_T " bytes\n",
162                    from, data->req.maxdownload));
163     }
164     DEBUGF(infof(data, "range-download from %" FORMAT_OFF_T
165                  " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
166                  from, to, data->req.maxdownload));
167   }
168   else
169     data->req.maxdownload = -1;
170   return CURLE_OK;
171 }
172
173 /*
174  * file_connect() gets called from Curl_protocol_connect() to allow us to
175  * do protocol-specific actions at connect-time.  We emulate a
176  * connect-then-transfer protocol and "connect" to the file here
177  */
178 static CURLcode file_connect(struct connectdata *conn, bool *done)
179 {
180   struct SessionHandle *data = conn->data;
181   char *real_path;
182   struct FILEPROTO *file;
183   int fd;
184 #ifdef DOS_FILESYSTEM
185   int i;
186   char *actual_path;
187 #endif
188
189   /* If there already is a protocol-specific struct allocated for this
190      sessionhandle, deal with it */
191   Curl_reset_reqproto(conn);
192
193   real_path = curl_easy_unescape(data, data->state.path, 0, NULL);
194   if(!real_path)
195     return CURLE_OUT_OF_MEMORY;
196
197   if(!data->state.proto.file) {
198     file = calloc(1, sizeof(struct FILEPROTO));
199     if(!file) {
200       free(real_path);
201       return CURLE_OUT_OF_MEMORY;
202     }
203     data->state.proto.file = file;
204   }
205   else {
206     /* file is not a protocol that can deal with "persistancy" */
207     file = data->state.proto.file;
208     Curl_safefree(file->freepath);
209     file->path = NULL;
210     if(file->fd != -1)
211       close(file->fd);
212     file->fd = -1;
213   }
214
215 #ifdef DOS_FILESYSTEM
216   /* If the first character is a slash, and there's
217      something that looks like a drive at the beginning of
218      the path, skip the slash.  If we remove the initial
219      slash in all cases, paths without drive letters end up
220      relative to the current directory which isn't how
221      browsers work.
222
223      Some browsers accept | instead of : as the drive letter
224      separator, so we do too.
225
226      On other platforms, we need the slash to indicate an
227      absolute pathname.  On Windows, absolute paths start
228      with a drive letter.
229   */
230   actual_path = real_path;
231   if((actual_path[0] == '/') &&
232       actual_path[1] &&
233      (actual_path[2] == ':' || actual_path[2] == '|')) {
234     actual_path[2] = ':';
235     actual_path++;
236   }
237
238   /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
239   for(i=0; actual_path[i] != '\0'; ++i)
240     if(actual_path[i] == '/')
241       actual_path[i] = '\\';
242
243   fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
244   file->path = actual_path;
245 #else
246   fd = open_readonly(real_path, O_RDONLY);
247   file->path = real_path;
248 #endif
249   file->freepath = real_path; /* free this when done */
250
251   file->fd = fd;
252   if(!data->set.upload && (fd == -1)) {
253     failf(data, "Couldn't open file %s", data->state.path);
254     file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
255     return CURLE_FILE_COULDNT_READ_FILE;
256   }
257   *done = TRUE;
258
259   return CURLE_OK;
260 }
261
262 static CURLcode file_done(struct connectdata *conn,
263                                CURLcode status, bool premature)
264 {
265   struct FILEPROTO *file = conn->data->state.proto.file;
266   (void)status; /* not used */
267   (void)premature; /* not used */
268
269   if(file) {
270     Curl_safefree(file->freepath);
271     file->path = NULL;
272     if(file->fd != -1)
273       close(file->fd);
274     file->fd = -1;
275   }
276
277   return CURLE_OK;
278 }
279
280 static CURLcode file_disconnect(struct connectdata *conn,
281                                 bool dead_connection)
282 {
283   struct FILEPROTO *file = conn->data->state.proto.file;
284   (void)dead_connection; /* not used */
285
286   if(file) {
287     Curl_safefree(file->freepath);
288     file->path = NULL;
289     if(file->fd != -1)
290       close(file->fd);
291     file->fd = -1;
292   }
293
294   return CURLE_OK;
295 }
296
297 #ifdef DOS_FILESYSTEM
298 #define DIRSEP '\\'
299 #else
300 #define DIRSEP '/'
301 #endif
302
303 static CURLcode file_upload(struct connectdata *conn)
304 {
305   struct FILEPROTO *file = conn->data->state.proto.file;
306   const char *dir = strchr(file->path, DIRSEP);
307   int fd;
308   int mode;
309   CURLcode res=CURLE_OK;
310   struct SessionHandle *data = conn->data;
311   char *buf = data->state.buffer;
312   size_t nread;
313   size_t nwrite;
314   curl_off_t bytecount = 0;
315   struct timeval now = Curl_tvnow();
316   struct_stat file_stat;
317   const char* buf2;
318
319   /*
320    * Since FILE: doesn't do the full init, we need to provide some extra
321    * assignments here.
322    */
323   conn->fread_func = data->set.fread_func;
324   conn->fread_in = data->set.in;
325   conn->data->req.upload_fromhere = buf;
326
327   if(!dir)
328     return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
329
330   if(!dir[1])
331     return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
332
333 #ifdef O_BINARY
334 #define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
335 #else
336 #define MODE_DEFAULT O_WRONLY|O_CREAT
337 #endif
338
339   if(data->state.resume_from)
340     mode = MODE_DEFAULT|O_APPEND;
341   else
342     mode = MODE_DEFAULT|O_TRUNC;
343
344   fd = open(file->path, mode, conn->data->set.new_file_perms);
345   if(fd < 0) {
346     failf(data, "Can't open %s for writing", file->path);
347     return CURLE_WRITE_ERROR;
348   }
349
350   if(-1 != data->set.infilesize)
351     /* known size of data to "upload" */
352     Curl_pgrsSetUploadSize(data, data->set.infilesize);
353
354   /* treat the negative resume offset value as the case of "-" */
355   if(data->state.resume_from < 0) {
356     if(fstat(fd, &file_stat)) {
357       close(fd);
358       failf(data, "Can't get the size of %s", file->path);
359       return CURLE_WRITE_ERROR;
360     }
361     else
362       data->state.resume_from = (curl_off_t)file_stat.st_size;
363   }
364
365   while(res == CURLE_OK) {
366     int readcount;
367     res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
368     if(res)
369       break;
370
371     if(readcount <= 0)  /* fix questionable compare error. curlvms */
372       break;
373
374     nread = (size_t)readcount;
375
376     /*skip bytes before resume point*/
377     if(data->state.resume_from) {
378       if((curl_off_t)nread <= data->state.resume_from ) {
379         data->state.resume_from -= nread;
380         nread = 0;
381         buf2 = buf;
382       }
383       else {
384         buf2 = buf + data->state.resume_from;
385         nread -= (size_t)data->state.resume_from;
386         data->state.resume_from = 0;
387       }
388     }
389     else
390       buf2 = buf;
391
392     /* write the data to the target */
393     nwrite = write(fd, buf2, nread);
394     if(nwrite != nread) {
395       res = CURLE_SEND_ERROR;
396       break;
397     }
398
399     bytecount += nread;
400
401     Curl_pgrsSetUploadCounter(data, bytecount);
402
403     if(Curl_pgrsUpdate(conn))
404       res = CURLE_ABORTED_BY_CALLBACK;
405     else
406       res = Curl_speedcheck(data, now);
407   }
408   if(!res && Curl_pgrsUpdate(conn))
409     res = CURLE_ABORTED_BY_CALLBACK;
410
411   close(fd);
412
413   return res;
414 }
415
416 /*
417  * file_do() is the protocol-specific function for the do-phase, separated
418  * from the connect-phase above. Other protocols merely setup the transfer in
419  * the do-phase, to have it done in the main transfer loop but since some
420  * platforms we support don't allow select()ing etc on file handles (as
421  * opposed to sockets) we instead perform the whole do-operation in this
422  * function.
423  */
424 static CURLcode file_do(struct connectdata *conn, bool *done)
425 {
426   /* This implementation ignores the host name in conformance with
427      RFC 1738. Only local files (reachable via the standard file system)
428      are supported. This means that files on remotely mounted directories
429      (via NFS, Samba, NT sharing) can be accessed through a file:// URL
430   */
431   CURLcode res = CURLE_OK;
432   struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
433                           Windows version to have a different struct without
434                           having to redefine the simple word 'stat' */
435   curl_off_t expected_size=0;
436   bool fstated=FALSE;
437   ssize_t nread;
438   struct SessionHandle *data = conn->data;
439   char *buf = data->state.buffer;
440   curl_off_t bytecount = 0;
441   int fd;
442   struct timeval now = Curl_tvnow();
443
444   *done = TRUE; /* unconditionally */
445
446   Curl_initinfo(data);
447   Curl_pgrsStartNow(data);
448
449   if(data->set.upload)
450     return file_upload(conn);
451
452   /* get the fd from the connection phase */
453   fd = conn->data->state.proto.file->fd;
454
455   /* VMS: This only works reliable for STREAMLF files */
456   if(-1 != fstat(fd, &statbuf)) {
457     /* we could stat it, then read out the size */
458     expected_size = statbuf.st_size;
459     /* and store the modification time */
460     data->info.filetime = (long)statbuf.st_mtime;
461     fstated = TRUE;
462   }
463
464   if(fstated && !data->state.range && data->set.timecondition) {
465     if(!Curl_meets_timecondition(data, (time_t)data->info.filetime)) {
466       *done = TRUE;
467       return CURLE_OK;
468     }
469   }
470
471   /* If we have selected NOBODY and HEADER, it means that we only want file
472      information. Which for FILE can't be much more than the file size and
473      date. */
474   if(data->set.opt_no_body && data->set.include_header && fstated) {
475     CURLcode result;
476     snprintf(buf, sizeof(data->state.buffer),
477              "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
478     result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
479     if(result)
480       return result;
481
482     result = Curl_client_write(conn, CLIENTWRITE_BOTH,
483                                (char *)"Accept-ranges: bytes\r\n", 0);
484     if(result)
485       return result;
486
487     if(fstated) {
488       time_t filetime = (time_t)statbuf.st_mtime;
489       struct tm buffer;
490       const struct tm *tm = &buffer;
491       result = Curl_gmtime(filetime, &buffer);
492       if(result)
493         return result;
494
495       /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
496       snprintf(buf, BUFSIZE-1,
497                "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
498                Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
499                tm->tm_mday,
500                Curl_month[tm->tm_mon],
501                tm->tm_year + 1900,
502                tm->tm_hour,
503                tm->tm_min,
504                tm->tm_sec);
505       result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
506     }
507     /* if we fstat()ed the file, set the file size to make it available post-
508        transfer */
509     if(fstated)
510       Curl_pgrsSetDownloadSize(data, expected_size);
511     return result;
512   }
513
514   /* Check whether file range has been specified */
515   file_range(conn);
516
517   /* Adjust the start offset in case we want to get the N last bytes
518    * of the stream iff the filesize could be determined */
519   if(data->state.resume_from < 0) {
520     if(!fstated) {
521       failf(data, "Can't get the size of file.");
522       return CURLE_READ_ERROR;
523     }
524     else
525       data->state.resume_from += (curl_off_t)statbuf.st_size;
526   }
527
528   if(data->state.resume_from <= expected_size)
529     expected_size -= data->state.resume_from;
530   else {
531     failf(data, "failed to resume file:// transfer");
532     return CURLE_BAD_DOWNLOAD_RESUME;
533   }
534
535   /* A high water mark has been specified so we obey... */
536   if(data->req.maxdownload > 0)
537     expected_size = data->req.maxdownload;
538
539   if(fstated && (expected_size == 0))
540     return CURLE_OK;
541
542   /* The following is a shortcut implementation of file reading
543      this is both more efficient than the former call to download() and
544      it avoids problems with select() and recv() on file descriptors
545      in Winsock */
546   if(fstated)
547     Curl_pgrsSetDownloadSize(data, expected_size);
548
549   if(data->state.resume_from) {
550     if(data->state.resume_from !=
551        lseek(fd, data->state.resume_from, SEEK_SET))
552       return CURLE_BAD_DOWNLOAD_RESUME;
553   }
554
555   Curl_pgrsTime(data, TIMER_STARTTRANSFER);
556
557   while(res == CURLE_OK) {
558     /* Don't fill a whole buffer if we want less than all data */
559     size_t bytestoread =
560       (expected_size < CURL_OFF_T_C(BUFSIZE) - CURL_OFF_T_C(1)) ?
561       curlx_sotouz(expected_size) : BUFSIZE - 1;
562
563     nread = read(fd, buf, bytestoread);
564
565     if(nread > 0)
566       buf[nread] = 0;
567
568     if(nread <= 0 || expected_size == 0)
569       break;
570
571     bytecount += nread;
572     expected_size -= nread;
573
574     res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
575     if(res)
576       return res;
577
578     Curl_pgrsSetDownloadCounter(data, bytecount);
579
580     if(Curl_pgrsUpdate(conn))
581       res = CURLE_ABORTED_BY_CALLBACK;
582     else
583       res = Curl_speedcheck(data, now);
584   }
585   if(Curl_pgrsUpdate(conn))
586     res = CURLE_ABORTED_BY_CALLBACK;
587
588   return res;
589 }
590
591 #endif