| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 | 
							- /***************************************************************************
 
-  *                                  _   _ ____  _
 
-  *  Project                     ___| | | |  _ \| |
 
-  *                             / __| | | | |_) | |
 
-  *                            | (__| |_| |  _ <| |___
 
-  *                             \___|\___/|_| \_\_____|
 
-  *
 
-  * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
 
-  *
 
-  * This software is licensed as described in the file COPYING, which
 
-  * you should have received as part of this distribution. The terms
 
-  * are also available at https://curl.se/docs/copyright.html.
 
-  *
 
-  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 
-  * copies of the Software, and permit persons to whom the Software is
 
-  * furnished to do so, under the terms of the COPYING file.
 
-  *
 
-  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 
-  * KIND, either express or implied.
 
-  *
 
-  * SPDX-License-Identifier: curl
 
-  *
 
-  ***************************************************************************/
 
- #include "curl_setup.h"
 
- #ifndef CURL_DISABLE_FILE
 
- #ifdef HAVE_NETINET_IN_H
 
- #include <netinet/in.h>
 
- #endif
 
- #ifdef HAVE_NETDB_H
 
- #include <netdb.h>
 
- #endif
 
- #ifdef HAVE_ARPA_INET_H
 
- #include <arpa/inet.h>
 
- #endif
 
- #ifdef HAVE_NET_IF_H
 
- #include <net/if.h>
 
- #endif
 
- #ifdef HAVE_SYS_IOCTL_H
 
- #include <sys/ioctl.h>
 
- #endif
 
- #ifdef HAVE_SYS_PARAM_H
 
- #include <sys/param.h>
 
- #endif
 
- #ifdef HAVE_FCNTL_H
 
- #include <fcntl.h>
 
- #endif
 
- #include "strtoofft.h"
 
- #include "urldata.h"
 
- #include <curl/curl.h>
 
- #include "progress.h"
 
- #include "sendf.h"
 
- #include "escape.h"
 
- #include "file.h"
 
- #include "speedcheck.h"
 
- #include "getinfo.h"
 
- #include "transfer.h"
 
- #include "url.h"
 
- #include "parsedate.h" /* for the week day and month names */
 
- #include "warnless.h"
 
- #include "curl_range.h"
 
- /* The last 3 #include files should be in this order */
 
- #include "curl_printf.h"
 
- #include "curl_memory.h"
 
- #include "memdebug.h"
 
- #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
 
- #define DOS_FILESYSTEM 1
 
- #elif defined(__amigaos4__)
 
- #define AMIGA_FILESYSTEM 1
 
- #endif
 
- #ifdef OPEN_NEEDS_ARG3
 
- #  define open_readonly(p,f) open((p),(f),(0))
 
- #else
 
- #  define open_readonly(p,f) open((p),(f))
 
- #endif
 
- /*
 
-  * Forward declarations.
 
-  */
 
- static CURLcode file_do(struct Curl_easy *data, bool *done);
 
- static CURLcode file_done(struct Curl_easy *data,
 
-                           CURLcode status, bool premature);
 
- static CURLcode file_connect(struct Curl_easy *data, bool *done);
 
- static CURLcode file_disconnect(struct Curl_easy *data,
 
-                                 struct connectdata *conn,
 
-                                 bool dead_connection);
 
- static CURLcode file_setup_connection(struct Curl_easy *data,
 
-                                       struct connectdata *conn);
 
- /*
 
-  * FILE scheme handler.
 
-  */
 
- const struct Curl_handler Curl_handler_file = {
 
-   "FILE",                               /* scheme */
 
-   file_setup_connection,                /* setup_connection */
 
-   file_do,                              /* do_it */
 
-   file_done,                            /* done */
 
-   ZERO_NULL,                            /* do_more */
 
-   file_connect,                         /* connect_it */
 
-   ZERO_NULL,                            /* connecting */
 
-   ZERO_NULL,                            /* doing */
 
-   ZERO_NULL,                            /* proto_getsock */
 
-   ZERO_NULL,                            /* doing_getsock */
 
-   ZERO_NULL,                            /* domore_getsock */
 
-   ZERO_NULL,                            /* perform_getsock */
 
-   file_disconnect,                      /* disconnect */
 
-   ZERO_NULL,                            /* readwrite */
 
-   ZERO_NULL,                            /* connection_check */
 
-   ZERO_NULL,                            /* attach connection */
 
-   0,                                    /* defport */
 
-   CURLPROTO_FILE,                       /* protocol */
 
-   CURLPROTO_FILE,                       /* family */
 
-   PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
 
- };
 
- static CURLcode file_setup_connection(struct Curl_easy *data,
 
-                                       struct connectdata *conn)
 
- {
 
-   (void)conn;
 
-   /* allocate the FILE specific struct */
 
-   data->req.p.file = calloc(1, sizeof(struct FILEPROTO));
 
-   if(!data->req.p.file)
 
-     return CURLE_OUT_OF_MEMORY;
 
-   return CURLE_OK;
 
- }
 
- /*
 
-  * file_connect() gets called from Curl_protocol_connect() to allow us to
 
-  * do protocol-specific actions at connect-time.  We emulate a
 
-  * connect-then-transfer protocol and "connect" to the file here
 
-  */
 
- static CURLcode file_connect(struct Curl_easy *data, bool *done)
 
- {
 
-   char *real_path;
 
-   struct FILEPROTO *file = data->req.p.file;
 
-   int fd;
 
- #ifdef DOS_FILESYSTEM
 
-   size_t i;
 
-   char *actual_path;
 
- #endif
 
-   size_t real_path_len;
 
-   CURLcode result;
 
-   if(file->path) {
 
-     /* already connected.
 
-      * the handler->connect_it() is normally only called once, but
 
-      * FILE does a special check on setting up the connection which
 
-      * calls this explicitly. */
 
-     *done = TRUE;
 
-     return CURLE_OK;
 
-   }
 
-   result = Curl_urldecode(data->state.up.path, 0, &real_path,
 
-                           &real_path_len, REJECT_ZERO);
 
-   if(result)
 
-     return result;
 
- #ifdef DOS_FILESYSTEM
 
-   /* If the first character is a slash, and there's
 
-      something that looks like a drive at the beginning of
 
-      the path, skip the slash.  If we remove the initial
 
-      slash in all cases, paths without drive letters end up
 
-      relative to the current directory which isn't how
 
-      browsers work.
 
-      Some browsers accept | instead of : as the drive letter
 
-      separator, so we do too.
 
-      On other platforms, we need the slash to indicate an
 
-      absolute pathname.  On Windows, absolute paths start
 
-      with a drive letter.
 
-   */
 
-   actual_path = real_path;
 
-   if((actual_path[0] == '/') &&
 
-       actual_path[1] &&
 
-      (actual_path[2] == ':' || actual_path[2] == '|')) {
 
-     actual_path[2] = ':';
 
-     actual_path++;
 
-     real_path_len--;
 
-   }
 
-   /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
 
-   for(i = 0; i < real_path_len; ++i)
 
-     if(actual_path[i] == '/')
 
-       actual_path[i] = '\\';
 
-     else if(!actual_path[i]) { /* binary zero */
 
-       Curl_safefree(real_path);
 
-       return CURLE_URL_MALFORMAT;
 
-     }
 
-   fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
 
-   file->path = actual_path;
 
- #else
 
-   if(memchr(real_path, 0, real_path_len)) {
 
-     /* binary zeroes indicate foul play */
 
-     Curl_safefree(real_path);
 
-     return CURLE_URL_MALFORMAT;
 
-   }
 
-   #ifdef AMIGA_FILESYSTEM
 
-   /*
 
-    * A leading slash in an AmigaDOS path denotes the parent
 
-    * directory, and hence we block this as it is relative.
 
-    * Absolute paths start with 'volumename:', so we check for
 
-    * this first. Failing that, we treat the path as a real unix
 
-    * path, but only if the application was compiled with -lunix.
 
-    */
 
-   fd = -1;
 
-   file->path = real_path;
 
-   if(real_path[0] == '/') {
 
-     extern int __unix_path_semantics;
 
-     if(strchr(real_path + 1, ':')) {
 
-       /* Amiga absolute path */
 
-       fd = open_readonly(real_path + 1, O_RDONLY);
 
-       file->path++;
 
-     }
 
-     else if(__unix_path_semantics) {
 
-       /* -lunix fallback */
 
-       fd = open_readonly(real_path, O_RDONLY);
 
-     }
 
-   }
 
-   #else
 
-   fd = open_readonly(real_path, O_RDONLY);
 
-   file->path = real_path;
 
-   #endif
 
- #endif
 
-   Curl_safefree(file->freepath);
 
-   file->freepath = real_path; /* free this when done */
 
-   file->fd = fd;
 
-   if(!data->state.upload && (fd == -1)) {
 
-     failf(data, "Couldn't open file %s", data->state.up.path);
 
-     file_done(data, CURLE_FILE_COULDNT_READ_FILE, FALSE);
 
-     return CURLE_FILE_COULDNT_READ_FILE;
 
-   }
 
-   *done = TRUE;
 
-   return CURLE_OK;
 
- }
 
- static CURLcode file_done(struct Curl_easy *data,
 
-                           CURLcode status, bool premature)
 
- {
 
-   struct FILEPROTO *file = data->req.p.file;
 
-   (void)status; /* not used */
 
-   (void)premature; /* not used */
 
-   if(file) {
 
-     Curl_safefree(file->freepath);
 
-     file->path = NULL;
 
-     if(file->fd != -1)
 
-       close(file->fd);
 
-     file->fd = -1;
 
-   }
 
-   return CURLE_OK;
 
- }
 
- static CURLcode file_disconnect(struct Curl_easy *data,
 
-                                 struct connectdata *conn,
 
-                                 bool dead_connection)
 
- {
 
-   (void)dead_connection; /* not used */
 
-   (void)conn;
 
-   return file_done(data, CURLE_OK, FALSE);
 
- }
 
- #ifdef DOS_FILESYSTEM
 
- #define DIRSEP '\\'
 
- #else
 
- #define DIRSEP '/'
 
- #endif
 
- static CURLcode file_upload(struct Curl_easy *data)
 
- {
 
-   struct FILEPROTO *file = data->req.p.file;
 
-   const char *dir = strchr(file->path, DIRSEP);
 
-   int fd;
 
-   int mode;
 
-   CURLcode result = CURLE_OK;
 
-   char *buf = data->state.buffer;
 
-   curl_off_t bytecount = 0;
 
-   struct_stat file_stat;
 
-   const char *buf2;
 
-   /*
 
-    * Since FILE: doesn't do the full init, we need to provide some extra
 
-    * assignments here.
 
-    */
 
-   data->req.upload_fromhere = buf;
 
-   if(!dir)
 
-     return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
 
-   if(!dir[1])
 
-     return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
 
- #ifdef O_BINARY
 
- #define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
 
- #else
 
- #define MODE_DEFAULT O_WRONLY|O_CREAT
 
- #endif
 
-   if(data->state.resume_from)
 
-     mode = MODE_DEFAULT|O_APPEND;
 
-   else
 
-     mode = MODE_DEFAULT|O_TRUNC;
 
-   fd = open(file->path, mode, data->set.new_file_perms);
 
-   if(fd < 0) {
 
-     failf(data, "Can't open %s for writing", file->path);
 
-     return CURLE_WRITE_ERROR;
 
-   }
 
-   if(-1 != data->state.infilesize)
 
-     /* known size of data to "upload" */
 
-     Curl_pgrsSetUploadSize(data, data->state.infilesize);
 
-   /* treat the negative resume offset value as the case of "-" */
 
-   if(data->state.resume_from < 0) {
 
-     if(fstat(fd, &file_stat)) {
 
-       close(fd);
 
-       failf(data, "Can't get the size of %s", file->path);
 
-       return CURLE_WRITE_ERROR;
 
-     }
 
-     data->state.resume_from = (curl_off_t)file_stat.st_size;
 
-   }
 
-   while(!result) {
 
-     size_t nread;
 
-     ssize_t nwrite;
 
-     size_t readcount;
 
-     result = Curl_fillreadbuffer(data, data->set.buffer_size, &readcount);
 
-     if(result)
 
-       break;
 
-     if(!readcount)
 
-       break;
 
-     nread = readcount;
 
-     /* skip bytes before resume point */
 
-     if(data->state.resume_from) {
 
-       if((curl_off_t)nread <= data->state.resume_from) {
 
-         data->state.resume_from -= nread;
 
-         nread = 0;
 
-         buf2 = buf;
 
-       }
 
-       else {
 
-         buf2 = buf + data->state.resume_from;
 
-         nread -= (size_t)data->state.resume_from;
 
-         data->state.resume_from = 0;
 
-       }
 
-     }
 
-     else
 
-       buf2 = buf;
 
-     /* write the data to the target */
 
-     nwrite = write(fd, buf2, nread);
 
-     if((size_t)nwrite != nread) {
 
-       result = CURLE_SEND_ERROR;
 
-       break;
 
-     }
 
-     bytecount += nread;
 
-     Curl_pgrsSetUploadCounter(data, bytecount);
 
-     if(Curl_pgrsUpdate(data))
 
-       result = CURLE_ABORTED_BY_CALLBACK;
 
-     else
 
-       result = Curl_speedcheck(data, Curl_now());
 
-   }
 
-   if(!result && Curl_pgrsUpdate(data))
 
-     result = CURLE_ABORTED_BY_CALLBACK;
 
-   close(fd);
 
-   return result;
 
- }
 
- /*
 
-  * file_do() is the protocol-specific function for the do-phase, separated
 
-  * from the connect-phase above. Other protocols merely setup the transfer in
 
-  * the do-phase, to have it done in the main transfer loop but since some
 
-  * platforms we support don't allow select()ing etc on file handles (as
 
-  * opposed to sockets) we instead perform the whole do-operation in this
 
-  * function.
 
-  */
 
- static CURLcode file_do(struct Curl_easy *data, bool *done)
 
- {
 
-   /* This implementation ignores the host name in conformance with
 
-      RFC 1738. Only local files (reachable via the standard file system)
 
-      are supported. This means that files on remotely mounted directories
 
-      (via NFS, Samba, NT sharing) can be accessed through a file:// URL
 
-   */
 
-   CURLcode result = CURLE_OK;
 
-   struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
 
-                           Windows version to have a different struct without
 
-                           having to redefine the simple word 'stat' */
 
-   curl_off_t expected_size = -1;
 
-   bool size_known;
 
-   bool fstated = FALSE;
 
-   char *buf = data->state.buffer;
 
-   curl_off_t bytecount = 0;
 
-   int fd;
 
-   struct FILEPROTO *file;
 
-   *done = TRUE; /* unconditionally */
 
-   Curl_pgrsStartNow(data);
 
-   if(data->state.upload)
 
-     return file_upload(data);
 
-   file = data->req.p.file;
 
-   /* get the fd from the connection phase */
 
-   fd = file->fd;
 
-   /* VMS: This only works reliable for STREAMLF files */
 
-   if(-1 != fstat(fd, &statbuf)) {
 
-     if(!S_ISDIR(statbuf.st_mode))
 
-       expected_size = statbuf.st_size;
 
-     /* and store the modification time */
 
-     data->info.filetime = statbuf.st_mtime;
 
-     fstated = TRUE;
 
-   }
 
-   if(fstated && !data->state.range && data->set.timecondition) {
 
-     if(!Curl_meets_timecondition(data, data->info.filetime)) {
 
-       *done = TRUE;
 
-       return CURLE_OK;
 
-     }
 
-   }
 
-   if(fstated) {
 
-     time_t filetime;
 
-     struct tm buffer;
 
-     const struct tm *tm = &buffer;
 
-     char header[80];
 
-     int headerlen;
 
-     char accept_ranges[24]= { "Accept-ranges: bytes\r\n" };
 
-     if(expected_size >= 0) {
 
-       headerlen = msnprintf(header, sizeof(header),
 
-                 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n",
 
-                 expected_size);
 
-       result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen);
 
-       if(result)
 
-         return result;
 
-       result = Curl_client_write(data, CLIENTWRITE_HEADER,
 
-                                  accept_ranges, strlen(accept_ranges));
 
-       if(result != CURLE_OK)
 
-         return result;
 
-     }
 
-     filetime = (time_t)statbuf.st_mtime;
 
-     result = Curl_gmtime(filetime, &buffer);
 
-     if(result)
 
-       return result;
 
-     /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
 
-     headerlen = msnprintf(header, sizeof(header),
 
-               "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n%s",
 
-               Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
 
-               tm->tm_mday,
 
-               Curl_month[tm->tm_mon],
 
-               tm->tm_year + 1900,
 
-               tm->tm_hour,
 
-               tm->tm_min,
 
-               tm->tm_sec,
 
-               data->req.no_body ? "": "\r\n");
 
-     result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen);
 
-     if(result)
 
-       return result;
 
-     /* set the file size to make it available post transfer */
 
-     Curl_pgrsSetDownloadSize(data, expected_size);
 
-     if(data->req.no_body)
 
-       return result;
 
-   }
 
-   /* Check whether file range has been specified */
 
-   result = Curl_range(data);
 
-   if(result)
 
-     return result;
 
-   /* Adjust the start offset in case we want to get the N last bytes
 
-    * of the stream if the filesize could be determined */
 
-   if(data->state.resume_from < 0) {
 
-     if(!fstated) {
 
-       failf(data, "Can't get the size of file.");
 
-       return CURLE_READ_ERROR;
 
-     }
 
-     data->state.resume_from += (curl_off_t)statbuf.st_size;
 
-   }
 
-   if(data->state.resume_from > 0) {
 
-     /* We check explicitly if we have a start offset, because
 
-      * expected_size may be -1 if we don't know how large the file is,
 
-      * in which case we should not adjust it. */
 
-     if(data->state.resume_from <= expected_size)
 
-       expected_size -= data->state.resume_from;
 
-     else {
 
-       failf(data, "failed to resume file:// transfer");
 
-       return CURLE_BAD_DOWNLOAD_RESUME;
 
-     }
 
-   }
 
-   /* A high water mark has been specified so we obey... */
 
-   if(data->req.maxdownload > 0)
 
-     expected_size = data->req.maxdownload;
 
-   if(!fstated || (expected_size <= 0))
 
-     size_known = FALSE;
 
-   else
 
-     size_known = TRUE;
 
-   /* The following is a shortcut implementation of file reading
 
-      this is both more efficient than the former call to download() and
 
-      it avoids problems with select() and recv() on file descriptors
 
-      in Winsock */
 
-   if(size_known)
 
-     Curl_pgrsSetDownloadSize(data, expected_size);
 
-   if(data->state.resume_from) {
 
-     if(data->state.resume_from !=
 
-        lseek(fd, data->state.resume_from, SEEK_SET))
 
-       return CURLE_BAD_DOWNLOAD_RESUME;
 
-   }
 
-   Curl_pgrsTime(data, TIMER_STARTTRANSFER);
 
-   while(!result) {
 
-     ssize_t nread;
 
-     /* Don't fill a whole buffer if we want less than all data */
 
-     size_t bytestoread;
 
-     if(size_known) {
 
-       bytestoread = (expected_size < data->set.buffer_size) ?
 
-         curlx_sotouz(expected_size) : (size_t)data->set.buffer_size;
 
-     }
 
-     else
 
-       bytestoread = data->set.buffer_size-1;
 
-     nread = read(fd, buf, bytestoread);
 
-     if(nread > 0)
 
-       buf[nread] = 0;
 
-     if(nread <= 0 || (size_known && (expected_size == 0)))
 
-       break;
 
-     bytecount += nread;
 
-     if(size_known)
 
-       expected_size -= nread;
 
-     result = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread);
 
-     if(result)
 
-       return result;
 
-     result = Curl_pgrsSetDownloadCounter(data, bytecount);
 
-     if(result)
 
-       return result;
 
-     if(Curl_pgrsUpdate(data))
 
-       result = CURLE_ABORTED_BY_CALLBACK;
 
-     else
 
-       result = Curl_speedcheck(data, Curl_now());
 
-   }
 
-   if(Curl_pgrsUpdate(data))
 
-     result = CURLE_ABORTED_BY_CALLBACK;
 
-   return result;
 
- }
 
- #endif
 
 
  |