113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#ifndef CURL_DISABLE_FILE
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
3013498266Sopenharmony_ci#include <netinet/in.h>
3113498266Sopenharmony_ci#endif
3213498266Sopenharmony_ci#ifdef HAVE_NETDB_H
3313498266Sopenharmony_ci#include <netdb.h>
3413498266Sopenharmony_ci#endif
3513498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H
3613498266Sopenharmony_ci#include <arpa/inet.h>
3713498266Sopenharmony_ci#endif
3813498266Sopenharmony_ci#ifdef HAVE_NET_IF_H
3913498266Sopenharmony_ci#include <net/if.h>
4013498266Sopenharmony_ci#endif
4113498266Sopenharmony_ci#ifdef HAVE_SYS_IOCTL_H
4213498266Sopenharmony_ci#include <sys/ioctl.h>
4313498266Sopenharmony_ci#endif
4413498266Sopenharmony_ci
4513498266Sopenharmony_ci#ifdef HAVE_SYS_PARAM_H
4613498266Sopenharmony_ci#include <sys/param.h>
4713498266Sopenharmony_ci#endif
4813498266Sopenharmony_ci
4913498266Sopenharmony_ci#ifdef HAVE_FCNTL_H
5013498266Sopenharmony_ci#include <fcntl.h>
5113498266Sopenharmony_ci#endif
5213498266Sopenharmony_ci
5313498266Sopenharmony_ci#include "strtoofft.h"
5413498266Sopenharmony_ci#include "urldata.h"
5513498266Sopenharmony_ci#include <curl/curl.h>
5613498266Sopenharmony_ci#include "progress.h"
5713498266Sopenharmony_ci#include "sendf.h"
5813498266Sopenharmony_ci#include "escape.h"
5913498266Sopenharmony_ci#include "file.h"
6013498266Sopenharmony_ci#include "speedcheck.h"
6113498266Sopenharmony_ci#include "getinfo.h"
6213498266Sopenharmony_ci#include "transfer.h"
6313498266Sopenharmony_ci#include "url.h"
6413498266Sopenharmony_ci#include "parsedate.h" /* for the week day and month names */
6513498266Sopenharmony_ci#include "warnless.h"
6613498266Sopenharmony_ci#include "curl_range.h"
6713498266Sopenharmony_ci/* The last 3 #include files should be in this order */
6813498266Sopenharmony_ci#include "curl_printf.h"
6913498266Sopenharmony_ci#include "curl_memory.h"
7013498266Sopenharmony_ci#include "memdebug.h"
7113498266Sopenharmony_ci
7213498266Sopenharmony_ci#if defined(_WIN32) || defined(MSDOS) || defined(__EMX__)
7313498266Sopenharmony_ci#define DOS_FILESYSTEM 1
7413498266Sopenharmony_ci#elif defined(__amigaos4__)
7513498266Sopenharmony_ci#define AMIGA_FILESYSTEM 1
7613498266Sopenharmony_ci#endif
7713498266Sopenharmony_ci
7813498266Sopenharmony_ci#ifdef OPEN_NEEDS_ARG3
7913498266Sopenharmony_ci#  define open_readonly(p,f) open((p),(f),(0))
8013498266Sopenharmony_ci#else
8113498266Sopenharmony_ci#  define open_readonly(p,f) open((p),(f))
8213498266Sopenharmony_ci#endif
8313498266Sopenharmony_ci
8413498266Sopenharmony_ci/*
8513498266Sopenharmony_ci * Forward declarations.
8613498266Sopenharmony_ci */
8713498266Sopenharmony_ci
8813498266Sopenharmony_cistatic CURLcode file_do(struct Curl_easy *data, bool *done);
8913498266Sopenharmony_cistatic CURLcode file_done(struct Curl_easy *data,
9013498266Sopenharmony_ci                          CURLcode status, bool premature);
9113498266Sopenharmony_cistatic CURLcode file_connect(struct Curl_easy *data, bool *done);
9213498266Sopenharmony_cistatic CURLcode file_disconnect(struct Curl_easy *data,
9313498266Sopenharmony_ci                                struct connectdata *conn,
9413498266Sopenharmony_ci                                bool dead_connection);
9513498266Sopenharmony_cistatic CURLcode file_setup_connection(struct Curl_easy *data,
9613498266Sopenharmony_ci                                      struct connectdata *conn);
9713498266Sopenharmony_ci
9813498266Sopenharmony_ci/*
9913498266Sopenharmony_ci * FILE scheme handler.
10013498266Sopenharmony_ci */
10113498266Sopenharmony_ci
10213498266Sopenharmony_ciconst struct Curl_handler Curl_handler_file = {
10313498266Sopenharmony_ci  "FILE",                               /* scheme */
10413498266Sopenharmony_ci  file_setup_connection,                /* setup_connection */
10513498266Sopenharmony_ci  file_do,                              /* do_it */
10613498266Sopenharmony_ci  file_done,                            /* done */
10713498266Sopenharmony_ci  ZERO_NULL,                            /* do_more */
10813498266Sopenharmony_ci  file_connect,                         /* connect_it */
10913498266Sopenharmony_ci  ZERO_NULL,                            /* connecting */
11013498266Sopenharmony_ci  ZERO_NULL,                            /* doing */
11113498266Sopenharmony_ci  ZERO_NULL,                            /* proto_getsock */
11213498266Sopenharmony_ci  ZERO_NULL,                            /* doing_getsock */
11313498266Sopenharmony_ci  ZERO_NULL,                            /* domore_getsock */
11413498266Sopenharmony_ci  ZERO_NULL,                            /* perform_getsock */
11513498266Sopenharmony_ci  file_disconnect,                      /* disconnect */
11613498266Sopenharmony_ci  ZERO_NULL,                            /* write_resp */
11713498266Sopenharmony_ci  ZERO_NULL,                            /* connection_check */
11813498266Sopenharmony_ci  ZERO_NULL,                            /* attach connection */
11913498266Sopenharmony_ci  0,                                    /* defport */
12013498266Sopenharmony_ci  CURLPROTO_FILE,                       /* protocol */
12113498266Sopenharmony_ci  CURLPROTO_FILE,                       /* family */
12213498266Sopenharmony_ci  PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
12313498266Sopenharmony_ci};
12413498266Sopenharmony_ci
12513498266Sopenharmony_ci
12613498266Sopenharmony_cistatic CURLcode file_setup_connection(struct Curl_easy *data,
12713498266Sopenharmony_ci                                      struct connectdata *conn)
12813498266Sopenharmony_ci{
12913498266Sopenharmony_ci  (void)conn;
13013498266Sopenharmony_ci  /* allocate the FILE specific struct */
13113498266Sopenharmony_ci  data->req.p.file = calloc(1, sizeof(struct FILEPROTO));
13213498266Sopenharmony_ci  if(!data->req.p.file)
13313498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
13413498266Sopenharmony_ci
13513498266Sopenharmony_ci  return CURLE_OK;
13613498266Sopenharmony_ci}
13713498266Sopenharmony_ci
13813498266Sopenharmony_ci/*
13913498266Sopenharmony_ci * file_connect() gets called from Curl_protocol_connect() to allow us to
14013498266Sopenharmony_ci * do protocol-specific actions at connect-time.  We emulate a
14113498266Sopenharmony_ci * connect-then-transfer protocol and "connect" to the file here
14213498266Sopenharmony_ci */
14313498266Sopenharmony_cistatic CURLcode file_connect(struct Curl_easy *data, bool *done)
14413498266Sopenharmony_ci{
14513498266Sopenharmony_ci  char *real_path;
14613498266Sopenharmony_ci  struct FILEPROTO *file = data->req.p.file;
14713498266Sopenharmony_ci  int fd;
14813498266Sopenharmony_ci#ifdef DOS_FILESYSTEM
14913498266Sopenharmony_ci  size_t i;
15013498266Sopenharmony_ci  char *actual_path;
15113498266Sopenharmony_ci#endif
15213498266Sopenharmony_ci  size_t real_path_len;
15313498266Sopenharmony_ci  CURLcode result;
15413498266Sopenharmony_ci
15513498266Sopenharmony_ci  if(file->path) {
15613498266Sopenharmony_ci    /* already connected.
15713498266Sopenharmony_ci     * the handler->connect_it() is normally only called once, but
15813498266Sopenharmony_ci     * FILE does a special check on setting up the connection which
15913498266Sopenharmony_ci     * calls this explicitly. */
16013498266Sopenharmony_ci    *done = TRUE;
16113498266Sopenharmony_ci    return CURLE_OK;
16213498266Sopenharmony_ci  }
16313498266Sopenharmony_ci
16413498266Sopenharmony_ci  result = Curl_urldecode(data->state.up.path, 0, &real_path,
16513498266Sopenharmony_ci                          &real_path_len, REJECT_ZERO);
16613498266Sopenharmony_ci  if(result)
16713498266Sopenharmony_ci    return result;
16813498266Sopenharmony_ci
16913498266Sopenharmony_ci#ifdef DOS_FILESYSTEM
17013498266Sopenharmony_ci  /* If the first character is a slash, and there's
17113498266Sopenharmony_ci     something that looks like a drive at the beginning of
17213498266Sopenharmony_ci     the path, skip the slash.  If we remove the initial
17313498266Sopenharmony_ci     slash in all cases, paths without drive letters end up
17413498266Sopenharmony_ci     relative to the current directory which isn't how
17513498266Sopenharmony_ci     browsers work.
17613498266Sopenharmony_ci
17713498266Sopenharmony_ci     Some browsers accept | instead of : as the drive letter
17813498266Sopenharmony_ci     separator, so we do too.
17913498266Sopenharmony_ci
18013498266Sopenharmony_ci     On other platforms, we need the slash to indicate an
18113498266Sopenharmony_ci     absolute pathname.  On Windows, absolute paths start
18213498266Sopenharmony_ci     with a drive letter.
18313498266Sopenharmony_ci  */
18413498266Sopenharmony_ci  actual_path = real_path;
18513498266Sopenharmony_ci  if((actual_path[0] == '/') &&
18613498266Sopenharmony_ci      actual_path[1] &&
18713498266Sopenharmony_ci     (actual_path[2] == ':' || actual_path[2] == '|')) {
18813498266Sopenharmony_ci    actual_path[2] = ':';
18913498266Sopenharmony_ci    actual_path++;
19013498266Sopenharmony_ci    real_path_len--;
19113498266Sopenharmony_ci  }
19213498266Sopenharmony_ci
19313498266Sopenharmony_ci  /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
19413498266Sopenharmony_ci  for(i = 0; i < real_path_len; ++i)
19513498266Sopenharmony_ci    if(actual_path[i] == '/')
19613498266Sopenharmony_ci      actual_path[i] = '\\';
19713498266Sopenharmony_ci    else if(!actual_path[i]) { /* binary zero */
19813498266Sopenharmony_ci      Curl_safefree(real_path);
19913498266Sopenharmony_ci      return CURLE_URL_MALFORMAT;
20013498266Sopenharmony_ci    }
20113498266Sopenharmony_ci
20213498266Sopenharmony_ci  fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
20313498266Sopenharmony_ci  file->path = actual_path;
20413498266Sopenharmony_ci#else
20513498266Sopenharmony_ci  if(memchr(real_path, 0, real_path_len)) {
20613498266Sopenharmony_ci    /* binary zeroes indicate foul play */
20713498266Sopenharmony_ci    Curl_safefree(real_path);
20813498266Sopenharmony_ci    return CURLE_URL_MALFORMAT;
20913498266Sopenharmony_ci  }
21013498266Sopenharmony_ci
21113498266Sopenharmony_ci  #ifdef AMIGA_FILESYSTEM
21213498266Sopenharmony_ci  /*
21313498266Sopenharmony_ci   * A leading slash in an AmigaDOS path denotes the parent
21413498266Sopenharmony_ci   * directory, and hence we block this as it is relative.
21513498266Sopenharmony_ci   * Absolute paths start with 'volumename:', so we check for
21613498266Sopenharmony_ci   * this first. Failing that, we treat the path as a real unix
21713498266Sopenharmony_ci   * path, but only if the application was compiled with -lunix.
21813498266Sopenharmony_ci   */
21913498266Sopenharmony_ci  fd = -1;
22013498266Sopenharmony_ci  file->path = real_path;
22113498266Sopenharmony_ci
22213498266Sopenharmony_ci  if(real_path[0] == '/') {
22313498266Sopenharmony_ci    extern int __unix_path_semantics;
22413498266Sopenharmony_ci    if(strchr(real_path + 1, ':')) {
22513498266Sopenharmony_ci      /* Amiga absolute path */
22613498266Sopenharmony_ci      fd = open_readonly(real_path + 1, O_RDONLY);
22713498266Sopenharmony_ci      file->path++;
22813498266Sopenharmony_ci    }
22913498266Sopenharmony_ci    else if(__unix_path_semantics) {
23013498266Sopenharmony_ci      /* -lunix fallback */
23113498266Sopenharmony_ci      fd = open_readonly(real_path, O_RDONLY);
23213498266Sopenharmony_ci    }
23313498266Sopenharmony_ci  }
23413498266Sopenharmony_ci  #else
23513498266Sopenharmony_ci  fd = open_readonly(real_path, O_RDONLY);
23613498266Sopenharmony_ci  file->path = real_path;
23713498266Sopenharmony_ci  #endif
23813498266Sopenharmony_ci#endif
23913498266Sopenharmony_ci  Curl_safefree(file->freepath);
24013498266Sopenharmony_ci  file->freepath = real_path; /* free this when done */
24113498266Sopenharmony_ci
24213498266Sopenharmony_ci  file->fd = fd;
24313498266Sopenharmony_ci  if(!data->state.upload && (fd == -1)) {
24413498266Sopenharmony_ci    failf(data, "Couldn't open file %s", data->state.up.path);
24513498266Sopenharmony_ci    file_done(data, CURLE_FILE_COULDNT_READ_FILE, FALSE);
24613498266Sopenharmony_ci    return CURLE_FILE_COULDNT_READ_FILE;
24713498266Sopenharmony_ci  }
24813498266Sopenharmony_ci  *done = TRUE;
24913498266Sopenharmony_ci
25013498266Sopenharmony_ci  return CURLE_OK;
25113498266Sopenharmony_ci}
25213498266Sopenharmony_ci
25313498266Sopenharmony_cistatic CURLcode file_done(struct Curl_easy *data,
25413498266Sopenharmony_ci                          CURLcode status, bool premature)
25513498266Sopenharmony_ci{
25613498266Sopenharmony_ci  struct FILEPROTO *file = data->req.p.file;
25713498266Sopenharmony_ci  (void)status; /* not used */
25813498266Sopenharmony_ci  (void)premature; /* not used */
25913498266Sopenharmony_ci
26013498266Sopenharmony_ci  if(file) {
26113498266Sopenharmony_ci    Curl_safefree(file->freepath);
26213498266Sopenharmony_ci    file->path = NULL;
26313498266Sopenharmony_ci    if(file->fd != -1)
26413498266Sopenharmony_ci      close(file->fd);
26513498266Sopenharmony_ci    file->fd = -1;
26613498266Sopenharmony_ci  }
26713498266Sopenharmony_ci
26813498266Sopenharmony_ci  return CURLE_OK;
26913498266Sopenharmony_ci}
27013498266Sopenharmony_ci
27113498266Sopenharmony_cistatic CURLcode file_disconnect(struct Curl_easy *data,
27213498266Sopenharmony_ci                                struct connectdata *conn,
27313498266Sopenharmony_ci                                bool dead_connection)
27413498266Sopenharmony_ci{
27513498266Sopenharmony_ci  (void)dead_connection; /* not used */
27613498266Sopenharmony_ci  (void)conn;
27713498266Sopenharmony_ci  return file_done(data, CURLE_OK, FALSE);
27813498266Sopenharmony_ci}
27913498266Sopenharmony_ci
28013498266Sopenharmony_ci#ifdef DOS_FILESYSTEM
28113498266Sopenharmony_ci#define DIRSEP '\\'
28213498266Sopenharmony_ci#else
28313498266Sopenharmony_ci#define DIRSEP '/'
28413498266Sopenharmony_ci#endif
28513498266Sopenharmony_ci
28613498266Sopenharmony_cistatic CURLcode file_upload(struct Curl_easy *data)
28713498266Sopenharmony_ci{
28813498266Sopenharmony_ci  struct FILEPROTO *file = data->req.p.file;
28913498266Sopenharmony_ci  const char *dir = strchr(file->path, DIRSEP);
29013498266Sopenharmony_ci  int fd;
29113498266Sopenharmony_ci  int mode;
29213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
29313498266Sopenharmony_ci  char buffer[8*1024], *uphere_save;
29413498266Sopenharmony_ci  curl_off_t bytecount = 0;
29513498266Sopenharmony_ci  struct_stat file_stat;
29613498266Sopenharmony_ci  const char *sendbuf;
29713498266Sopenharmony_ci
29813498266Sopenharmony_ci  /*
29913498266Sopenharmony_ci   * Since FILE: doesn't do the full init, we need to provide some extra
30013498266Sopenharmony_ci   * assignments here.
30113498266Sopenharmony_ci   */
30213498266Sopenharmony_ci
30313498266Sopenharmony_ci  if(!dir)
30413498266Sopenharmony_ci    return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
30513498266Sopenharmony_ci
30613498266Sopenharmony_ci  if(!dir[1])
30713498266Sopenharmony_ci    return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
30813498266Sopenharmony_ci
30913498266Sopenharmony_ci#ifdef O_BINARY
31013498266Sopenharmony_ci#define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
31113498266Sopenharmony_ci#else
31213498266Sopenharmony_ci#define MODE_DEFAULT O_WRONLY|O_CREAT
31313498266Sopenharmony_ci#endif
31413498266Sopenharmony_ci
31513498266Sopenharmony_ci  if(data->state.resume_from)
31613498266Sopenharmony_ci    mode = MODE_DEFAULT|O_APPEND;
31713498266Sopenharmony_ci  else
31813498266Sopenharmony_ci    mode = MODE_DEFAULT|O_TRUNC;
31913498266Sopenharmony_ci
32013498266Sopenharmony_ci  fd = open(file->path, mode, data->set.new_file_perms);
32113498266Sopenharmony_ci  if(fd < 0) {
32213498266Sopenharmony_ci    failf(data, "Can't open %s for writing", file->path);
32313498266Sopenharmony_ci    return CURLE_WRITE_ERROR;
32413498266Sopenharmony_ci  }
32513498266Sopenharmony_ci
32613498266Sopenharmony_ci  if(-1 != data->state.infilesize)
32713498266Sopenharmony_ci    /* known size of data to "upload" */
32813498266Sopenharmony_ci    Curl_pgrsSetUploadSize(data, data->state.infilesize);
32913498266Sopenharmony_ci
33013498266Sopenharmony_ci  /* treat the negative resume offset value as the case of "-" */
33113498266Sopenharmony_ci  if(data->state.resume_from < 0) {
33213498266Sopenharmony_ci    if(fstat(fd, &file_stat)) {
33313498266Sopenharmony_ci      close(fd);
33413498266Sopenharmony_ci      failf(data, "Can't get the size of %s", file->path);
33513498266Sopenharmony_ci      return CURLE_WRITE_ERROR;
33613498266Sopenharmony_ci    }
33713498266Sopenharmony_ci    data->state.resume_from = (curl_off_t)file_stat.st_size;
33813498266Sopenharmony_ci  }
33913498266Sopenharmony_ci
34013498266Sopenharmony_ci  /* Yikes! Curl_fillreadbuffer uses data->req.upload_fromhere to READ
34113498266Sopenharmony_ci   * client data to! Please, someone fix... */
34213498266Sopenharmony_ci  uphere_save = data->req.upload_fromhere;
34313498266Sopenharmony_ci  while(!result) {
34413498266Sopenharmony_ci    size_t nread;
34513498266Sopenharmony_ci    ssize_t nwrite;
34613498266Sopenharmony_ci    size_t readcount;
34713498266Sopenharmony_ci    data->req.upload_fromhere = buffer;
34813498266Sopenharmony_ci    result = Curl_fillreadbuffer(data, sizeof(buffer), &readcount);
34913498266Sopenharmony_ci    if(result)
35013498266Sopenharmony_ci      break;
35113498266Sopenharmony_ci
35213498266Sopenharmony_ci    if(!readcount)
35313498266Sopenharmony_ci      break;
35413498266Sopenharmony_ci
35513498266Sopenharmony_ci    nread = readcount;
35613498266Sopenharmony_ci
35713498266Sopenharmony_ci    /* skip bytes before resume point */
35813498266Sopenharmony_ci    if(data->state.resume_from) {
35913498266Sopenharmony_ci      if((curl_off_t)nread <= data->state.resume_from) {
36013498266Sopenharmony_ci        data->state.resume_from -= nread;
36113498266Sopenharmony_ci        nread = 0;
36213498266Sopenharmony_ci        sendbuf = buffer;
36313498266Sopenharmony_ci      }
36413498266Sopenharmony_ci      else {
36513498266Sopenharmony_ci        sendbuf = buffer + data->state.resume_from;
36613498266Sopenharmony_ci        nread -= (size_t)data->state.resume_from;
36713498266Sopenharmony_ci        data->state.resume_from = 0;
36813498266Sopenharmony_ci      }
36913498266Sopenharmony_ci    }
37013498266Sopenharmony_ci    else
37113498266Sopenharmony_ci      sendbuf = buffer;
37213498266Sopenharmony_ci
37313498266Sopenharmony_ci    /* write the data to the target */
37413498266Sopenharmony_ci    nwrite = write(fd, sendbuf, nread);
37513498266Sopenharmony_ci    if((size_t)nwrite != nread) {
37613498266Sopenharmony_ci      result = CURLE_SEND_ERROR;
37713498266Sopenharmony_ci      break;
37813498266Sopenharmony_ci    }
37913498266Sopenharmony_ci
38013498266Sopenharmony_ci    bytecount += nread;
38113498266Sopenharmony_ci
38213498266Sopenharmony_ci    Curl_pgrsSetUploadCounter(data, bytecount);
38313498266Sopenharmony_ci
38413498266Sopenharmony_ci    if(Curl_pgrsUpdate(data))
38513498266Sopenharmony_ci      result = CURLE_ABORTED_BY_CALLBACK;
38613498266Sopenharmony_ci    else
38713498266Sopenharmony_ci      result = Curl_speedcheck(data, Curl_now());
38813498266Sopenharmony_ci  }
38913498266Sopenharmony_ci  if(!result && Curl_pgrsUpdate(data))
39013498266Sopenharmony_ci    result = CURLE_ABORTED_BY_CALLBACK;
39113498266Sopenharmony_ci
39213498266Sopenharmony_ci  close(fd);
39313498266Sopenharmony_ci  data->req.upload_fromhere = uphere_save;
39413498266Sopenharmony_ci
39513498266Sopenharmony_ci  return result;
39613498266Sopenharmony_ci}
39713498266Sopenharmony_ci
39813498266Sopenharmony_ci/*
39913498266Sopenharmony_ci * file_do() is the protocol-specific function for the do-phase, separated
40013498266Sopenharmony_ci * from the connect-phase above. Other protocols merely setup the transfer in
40113498266Sopenharmony_ci * the do-phase, to have it done in the main transfer loop but since some
40213498266Sopenharmony_ci * platforms we support don't allow select()ing etc on file handles (as
40313498266Sopenharmony_ci * opposed to sockets) we instead perform the whole do-operation in this
40413498266Sopenharmony_ci * function.
40513498266Sopenharmony_ci */
40613498266Sopenharmony_cistatic CURLcode file_do(struct Curl_easy *data, bool *done)
40713498266Sopenharmony_ci{
40813498266Sopenharmony_ci  /* This implementation ignores the host name in conformance with
40913498266Sopenharmony_ci     RFC 1738. Only local files (reachable via the standard file system)
41013498266Sopenharmony_ci     are supported. This means that files on remotely mounted directories
41113498266Sopenharmony_ci     (via NFS, Samba, NT sharing) can be accessed through a file:// URL
41213498266Sopenharmony_ci  */
41313498266Sopenharmony_ci  CURLcode result = CURLE_OK;
41413498266Sopenharmony_ci  struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
41513498266Sopenharmony_ci                          Windows version to have a different struct without
41613498266Sopenharmony_ci                          having to redefine the simple word 'stat' */
41713498266Sopenharmony_ci  curl_off_t expected_size = -1;
41813498266Sopenharmony_ci  bool size_known;
41913498266Sopenharmony_ci  bool fstated = FALSE;
42013498266Sopenharmony_ci  int fd;
42113498266Sopenharmony_ci  struct FILEPROTO *file;
42213498266Sopenharmony_ci
42313498266Sopenharmony_ci  *done = TRUE; /* unconditionally */
42413498266Sopenharmony_ci
42513498266Sopenharmony_ci  if(data->state.upload)
42613498266Sopenharmony_ci    return file_upload(data);
42713498266Sopenharmony_ci
42813498266Sopenharmony_ci  file = data->req.p.file;
42913498266Sopenharmony_ci
43013498266Sopenharmony_ci  /* get the fd from the connection phase */
43113498266Sopenharmony_ci  fd = file->fd;
43213498266Sopenharmony_ci
43313498266Sopenharmony_ci  /* VMS: This only works reliable for STREAMLF files */
43413498266Sopenharmony_ci  if(-1 != fstat(fd, &statbuf)) {
43513498266Sopenharmony_ci    if(!S_ISDIR(statbuf.st_mode))
43613498266Sopenharmony_ci      expected_size = statbuf.st_size;
43713498266Sopenharmony_ci    /* and store the modification time */
43813498266Sopenharmony_ci    data->info.filetime = statbuf.st_mtime;
43913498266Sopenharmony_ci    fstated = TRUE;
44013498266Sopenharmony_ci  }
44113498266Sopenharmony_ci
44213498266Sopenharmony_ci  if(fstated && !data->state.range && data->set.timecondition) {
44313498266Sopenharmony_ci    if(!Curl_meets_timecondition(data, data->info.filetime)) {
44413498266Sopenharmony_ci      *done = TRUE;
44513498266Sopenharmony_ci      return CURLE_OK;
44613498266Sopenharmony_ci    }
44713498266Sopenharmony_ci  }
44813498266Sopenharmony_ci
44913498266Sopenharmony_ci  if(fstated) {
45013498266Sopenharmony_ci    time_t filetime;
45113498266Sopenharmony_ci    struct tm buffer;
45213498266Sopenharmony_ci    const struct tm *tm = &buffer;
45313498266Sopenharmony_ci    char header[80];
45413498266Sopenharmony_ci    int headerlen;
45513498266Sopenharmony_ci    char accept_ranges[24]= { "Accept-ranges: bytes\r\n" };
45613498266Sopenharmony_ci    if(expected_size >= 0) {
45713498266Sopenharmony_ci      headerlen = msnprintf(header, sizeof(header),
45813498266Sopenharmony_ci                "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n",
45913498266Sopenharmony_ci                expected_size);
46013498266Sopenharmony_ci      result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen);
46113498266Sopenharmony_ci      if(result)
46213498266Sopenharmony_ci        return result;
46313498266Sopenharmony_ci
46413498266Sopenharmony_ci      result = Curl_client_write(data, CLIENTWRITE_HEADER,
46513498266Sopenharmony_ci                                 accept_ranges, strlen(accept_ranges));
46613498266Sopenharmony_ci      if(result != CURLE_OK)
46713498266Sopenharmony_ci        return result;
46813498266Sopenharmony_ci    }
46913498266Sopenharmony_ci
47013498266Sopenharmony_ci    filetime = (time_t)statbuf.st_mtime;
47113498266Sopenharmony_ci    result = Curl_gmtime(filetime, &buffer);
47213498266Sopenharmony_ci    if(result)
47313498266Sopenharmony_ci      return result;
47413498266Sopenharmony_ci
47513498266Sopenharmony_ci    /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
47613498266Sopenharmony_ci    headerlen = msnprintf(header, sizeof(header),
47713498266Sopenharmony_ci              "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n%s",
47813498266Sopenharmony_ci              Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
47913498266Sopenharmony_ci              tm->tm_mday,
48013498266Sopenharmony_ci              Curl_month[tm->tm_mon],
48113498266Sopenharmony_ci              tm->tm_year + 1900,
48213498266Sopenharmony_ci              tm->tm_hour,
48313498266Sopenharmony_ci              tm->tm_min,
48413498266Sopenharmony_ci              tm->tm_sec,
48513498266Sopenharmony_ci              data->req.no_body ? "": "\r\n");
48613498266Sopenharmony_ci    result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen);
48713498266Sopenharmony_ci    if(result)
48813498266Sopenharmony_ci      return result;
48913498266Sopenharmony_ci    /* set the file size to make it available post transfer */
49013498266Sopenharmony_ci    Curl_pgrsSetDownloadSize(data, expected_size);
49113498266Sopenharmony_ci    if(data->req.no_body)
49213498266Sopenharmony_ci      return result;
49313498266Sopenharmony_ci  }
49413498266Sopenharmony_ci
49513498266Sopenharmony_ci  /* Check whether file range has been specified */
49613498266Sopenharmony_ci  result = Curl_range(data);
49713498266Sopenharmony_ci  if(result)
49813498266Sopenharmony_ci    return result;
49913498266Sopenharmony_ci
50013498266Sopenharmony_ci  /* Adjust the start offset in case we want to get the N last bytes
50113498266Sopenharmony_ci   * of the stream if the filesize could be determined */
50213498266Sopenharmony_ci  if(data->state.resume_from < 0) {
50313498266Sopenharmony_ci    if(!fstated) {
50413498266Sopenharmony_ci      failf(data, "Can't get the size of file.");
50513498266Sopenharmony_ci      return CURLE_READ_ERROR;
50613498266Sopenharmony_ci    }
50713498266Sopenharmony_ci    data->state.resume_from += (curl_off_t)statbuf.st_size;
50813498266Sopenharmony_ci  }
50913498266Sopenharmony_ci
51013498266Sopenharmony_ci  if(data->state.resume_from > 0) {
51113498266Sopenharmony_ci    /* We check explicitly if we have a start offset, because
51213498266Sopenharmony_ci     * expected_size may be -1 if we don't know how large the file is,
51313498266Sopenharmony_ci     * in which case we should not adjust it. */
51413498266Sopenharmony_ci    if(data->state.resume_from <= expected_size)
51513498266Sopenharmony_ci      expected_size -= data->state.resume_from;
51613498266Sopenharmony_ci    else {
51713498266Sopenharmony_ci      failf(data, "failed to resume file:// transfer");
51813498266Sopenharmony_ci      return CURLE_BAD_DOWNLOAD_RESUME;
51913498266Sopenharmony_ci    }
52013498266Sopenharmony_ci  }
52113498266Sopenharmony_ci
52213498266Sopenharmony_ci  /* A high water mark has been specified so we obey... */
52313498266Sopenharmony_ci  if(data->req.maxdownload > 0)
52413498266Sopenharmony_ci    expected_size = data->req.maxdownload;
52513498266Sopenharmony_ci
52613498266Sopenharmony_ci  if(!fstated || (expected_size <= 0))
52713498266Sopenharmony_ci    size_known = FALSE;
52813498266Sopenharmony_ci  else
52913498266Sopenharmony_ci    size_known = TRUE;
53013498266Sopenharmony_ci
53113498266Sopenharmony_ci  /* The following is a shortcut implementation of file reading
53213498266Sopenharmony_ci     this is both more efficient than the former call to download() and
53313498266Sopenharmony_ci     it avoids problems with select() and recv() on file descriptors
53413498266Sopenharmony_ci     in Winsock */
53513498266Sopenharmony_ci  if(size_known)
53613498266Sopenharmony_ci    Curl_pgrsSetDownloadSize(data, expected_size);
53713498266Sopenharmony_ci
53813498266Sopenharmony_ci  if(data->state.resume_from) {
53913498266Sopenharmony_ci    if(data->state.resume_from !=
54013498266Sopenharmony_ci       lseek(fd, data->state.resume_from, SEEK_SET))
54113498266Sopenharmony_ci      return CURLE_BAD_DOWNLOAD_RESUME;
54213498266Sopenharmony_ci  }
54313498266Sopenharmony_ci
54413498266Sopenharmony_ci  Curl_pgrsTime(data, TIMER_STARTTRANSFER);
54513498266Sopenharmony_ci
54613498266Sopenharmony_ci  while(!result) {
54713498266Sopenharmony_ci    char tmpbuf[8*1024];
54813498266Sopenharmony_ci    ssize_t nread;
54913498266Sopenharmony_ci    /* Don't fill a whole buffer if we want less than all data */
55013498266Sopenharmony_ci    size_t bytestoread;
55113498266Sopenharmony_ci
55213498266Sopenharmony_ci    if(size_known) {
55313498266Sopenharmony_ci      bytestoread = (expected_size < (curl_off_t)(sizeof(tmpbuf)-1)) ?
55413498266Sopenharmony_ci        curlx_sotouz(expected_size) : (sizeof(tmpbuf)-1);
55513498266Sopenharmony_ci    }
55613498266Sopenharmony_ci    else
55713498266Sopenharmony_ci      bytestoread = sizeof(tmpbuf)-1;
55813498266Sopenharmony_ci
55913498266Sopenharmony_ci    nread = read(fd, tmpbuf, bytestoread);
56013498266Sopenharmony_ci
56113498266Sopenharmony_ci    if(nread > 0)
56213498266Sopenharmony_ci      tmpbuf[nread] = 0;
56313498266Sopenharmony_ci
56413498266Sopenharmony_ci    if(nread <= 0 || (size_known && (expected_size == 0)))
56513498266Sopenharmony_ci      break;
56613498266Sopenharmony_ci
56713498266Sopenharmony_ci    if(size_known)
56813498266Sopenharmony_ci      expected_size -= nread;
56913498266Sopenharmony_ci
57013498266Sopenharmony_ci    result = Curl_client_write(data, CLIENTWRITE_BODY, tmpbuf, nread);
57113498266Sopenharmony_ci    if(result)
57213498266Sopenharmony_ci      return result;
57313498266Sopenharmony_ci
57413498266Sopenharmony_ci    if(Curl_pgrsUpdate(data))
57513498266Sopenharmony_ci      result = CURLE_ABORTED_BY_CALLBACK;
57613498266Sopenharmony_ci    else
57713498266Sopenharmony_ci      result = Curl_speedcheck(data, Curl_now());
57813498266Sopenharmony_ci  }
57913498266Sopenharmony_ci  if(Curl_pgrsUpdate(data))
58013498266Sopenharmony_ci    result = CURLE_ABORTED_BY_CALLBACK;
58113498266Sopenharmony_ci
58213498266Sopenharmony_ci  return result;
58313498266Sopenharmony_ci}
58413498266Sopenharmony_ci
58513498266Sopenharmony_ci#endif
586