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