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