#include "setup.h"
#ifndef CURL_DISABLE_FILE
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef WIN32
#include <time.h>
#include <io.h>
#include <fcntl.h>
#else
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.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
#include <sys/ioctl.h>
#include <signal.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#endif
#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 "memory.h"
#include "parsedate.h"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#include "memdebug.h"
CURLcode Curl_file_connect(struct connectdata *conn)
{
char *real_path = curl_easy_unescape(conn->data, conn->data->reqdata.path, 0, NULL);
struct FILEPROTO *file;
int fd;
#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
int i;
char *actual_path;
#endif
if(!real_path)
return CURLE_OUT_OF_MEMORY;
file = (struct FILEPROTO *)calloc(sizeof(struct FILEPROTO), 1);
if(!file) {
free(real_path);
return CURLE_OUT_OF_MEMORY;
}
if (conn->data->reqdata.proto.file) {
free(conn->data->reqdata.proto.file);
}
conn->data->reqdata.proto.file = file;
#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
actual_path = real_path;
if ((actual_path[0] == '/') &&
actual_path[1] &&
(actual_path[2] == ':' || actual_path[2] == '|'))
{
actual_path[2] = ':';
actual_path++;
}
for (i=0; actual_path[i] != '\0'; ++i)
if (actual_path[i] == '/')
actual_path[i] = '\\';
fd = open(actual_path, O_RDONLY | O_BINARY);
file->path = actual_path;
#else
fd = open(real_path, O_RDONLY);
file->path = real_path;
#endif
file->freepath = real_path;
file->fd = fd;
if(!conn->data->set.upload && (fd == -1)) {
failf(conn->data, "Couldn't open file %s", conn->data->reqdata.path);
Curl_file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
return CURLE_FILE_COULDNT_READ_FILE;
}
return CURLE_OK;
}
CURLcode Curl_file_done(struct connectdata *conn,
CURLcode status, bool premature)
{
struct FILEPROTO *file = conn->data->reqdata.proto.file;
(void)status;
(void)premature;
Curl_safefree(file->freepath);
if(file->fd != -1)
close(file->fd);
return CURLE_OK;
}
#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
#define DIRSEP '\\'
#else
#define DIRSEP '/'
#endif
static CURLcode file_upload(struct connectdata *conn)
{
struct FILEPROTO *file = conn->data->reqdata.proto.file;
char *dir = strchr(file->path, DIRSEP);
FILE *fp;
CURLcode res=CURLE_OK;
struct SessionHandle *data = conn->data;
char *buf = data->state.buffer;
size_t nread;
size_t nwrite;
curl_off_t bytecount = 0;
struct timeval now = Curl_tvnow();
struct_stat file_stat;
char* buf2;
conn->fread = data->set.fread;
conn->fread_in = data->set.in;
conn->data->reqdata.upload_fromhere = buf;
if(!dir)
return CURLE_FILE_COULDNT_READ_FILE;
if(!dir[1])
return CURLE_FILE_COULDNT_READ_FILE;
if(data->reqdata.resume_from)
fp = fopen( file->path, "ab" );
else
fp = fopen(file->path, "wb");
if(!fp) {
failf(data, "Can't open %s for writing", file->path);
return CURLE_WRITE_ERROR;
}
if(-1 != data->set.infilesize)
Curl_pgrsSetUploadSize(data, data->set.infilesize);
if(data->reqdata.resume_from < 0){
if(stat(file->path, &file_stat)){
fclose(fp);
failf(data, "Can't get the size of %s", file->path);
return CURLE_WRITE_ERROR;
}
else
data->reqdata.resume_from = (curl_off_t)file_stat.st_size;
}
while (res == CURLE_OK) {
int readcount;
res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
if(res)
break;
if (readcount <= 0)
break;
nread = (size_t)readcount;
if(data->reqdata.resume_from) {
if( (curl_off_t)nread <= data->reqdata.resume_from ) {
data->reqdata.resume_from -= nread;
nread = 0;
buf2 = buf;
}
else {
buf2 = buf + data->reqdata.resume_from;
nread -= data->reqdata.resume_from;
data->reqdata.resume_from = 0;
}
}
else
buf2 = buf;
nwrite = fwrite(buf2, 1, nread, fp);
if(nwrite != nread) {
res = CURLE_SEND_ERROR;
break;
}
bytecount += nread;
Curl_pgrsSetUploadCounter(data, bytecount);
if(Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
else
res = Curl_speedcheck(data, now);
}
if(!res && Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
fclose(fp);
return res;
}
CURLcode Curl_file(struct connectdata *conn, bool *done)
{
CURLcode res = CURLE_OK;
struct_stat statbuf;
curl_off_t expected_size=0;
bool fstated=FALSE;
ssize_t nread;
struct SessionHandle *data = conn->data;
char *buf = data->state.buffer;
curl_off_t bytecount = 0;
int fd;
struct timeval now = Curl_tvnow();
*done = TRUE;
Curl_readwrite_init(conn);
Curl_initinfo(data);
Curl_pgrsStartNow(data);
if(data->set.upload)
return file_upload(conn);
fd = conn->data->reqdata.proto.file->fd;
if( -1 != fstat(fd, &statbuf)) {
expected_size = statbuf.st_size;
fstated = TRUE;
}
if(conn->bits.no_body && data->set.include_header && fstated) {
CURLcode result;
snprintf(buf, sizeof(data->state.buffer),
"Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
if(result)
return result;
result = Curl_client_write(conn, CLIENTWRITE_BOTH,
(char *)"Accept-ranges: bytes\r\n", 0);
if(result)
return result;
if(fstated) {
struct tm *tm;
time_t clock = (time_t)statbuf.st_mtime;
#ifdef HAVE_GMTIME_R
struct tm buffer;
tm = (struct tm *)gmtime_r(&clock, &buffer);
#else
tm = gmtime(&clock);
#endif
snprintf(buf, BUFSIZE-1,
"Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
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);
result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
}
return result;
}
if (data->reqdata.resume_from <= expected_size)
expected_size -= data->reqdata.resume_from;
else {
failf(data, "failed to resume file:// transfer");
return CURLE_BAD_DOWNLOAD_RESUME;
}
if (fstated && (expected_size == 0))
return CURLE_OK;
if(fstated)
Curl_pgrsSetDownloadSize(data, expected_size);
if(data->reqdata.resume_from) {
if(data->reqdata.resume_from !=
lseek(fd, data->reqdata.resume_from, SEEK_SET))
return CURLE_BAD_DOWNLOAD_RESUME;
}
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
while (res == CURLE_OK) {
nread = read(fd, buf, BUFSIZE-1);
if ( nread > 0)
buf[nread] = 0;
if (nread <= 0)
break;
bytecount += nread;
res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
if(res)
return res;
Curl_pgrsSetDownloadCounter(data, bytecount);
if(Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
else
res = Curl_speedcheck(data, now);
}
if(Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
return res;
}
#endif