///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
//
// See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
#define CPPCMS_SOURCE
#include <cppcms/http_file.h>
#include <cppcms/urandom.h>
#include <cppcms/cppcms_error.h>
#include <booster/nowide/cstdio.h>
#include <booster/nowide/fstream.h>
#include <stdlib.h>
#include <iostream>
#include "tohex.h"
namespace cppcms {
namespace http {
struct file::impl_data { long long size; };
std::string file::name() const
{
return name_;
}
std::string file::mime() const
{
return mime_;
}
bool file::has_mime() const
{
return !mime_.empty();
}
std::string file::filename() const
{
return filename_;
}
long long file::size()
{
return d->size;
}
std::istream &file::data()
{
if(saved_in_file_)
return file_;
else
return file_data_;
}
std::ostream &file::write_data()
{
if(saved_in_file_) {
return file_;
}
else {
if(size() > static_cast<long long>(size_limit_)) {
move_to_file();
return file_;
}
else {
return file_data_;
}
}
}
void file::copy_stream(std::istream &in,std::ostream &out)
{
out << in.rdbuf();
}
void file::save_to(std::string const &filename)
{
if(!saved_in_file_) {
file_data_.clear();
file_data_.seekg(0);
save_by_copy(filename,file_data_);
return;
}
file_.clear();
file_.seekg(0);
file_.sync();
#ifdef CPPCMS_WIN32
file_.close();
/// we can't move opened file on windows as it would be locked
if(booster::nowide::rename(tmp_file_name_.c_str(),filename.c_str())!=0) {
file_.open(tmp_file_name_.c_str(),std::ios_base::binary | std::ios_base::in | std::ios_base::out);
if(!file_) {
throw cppcms_error("Failed to reopen file");
}
save_by_copy(filename,file_);
file_.close();
booster::nowide::remove(tmp_file_name_.c_str());
}
#else
if(booster::nowide::rename(tmp_file_name_.c_str(),filename.c_str())!=0) {
save_by_copy(filename,file_);
booster::nowide::remove(tmp_file_name_.c_str());
}
file_.close();
#endif
removed_ = 1;
}
void file::save_by_copy(std::string const &file_name,std::istream &in)
{
booster::nowide::ofstream f(file_name.c_str(),std::ios_base::binary | std::ios_base::out);
if(!f) {
throw cppcms_error("Failed to save open file:"+file_name);
}
copy_stream(in,f);
f << std::flush;
f.close();
}
void file::set_memory_limit(size_t size)
{
size_limit_ = size;
}
void file::set_temporary_directory(std::string const &d)
{
temporary_dir_ = d;
}
void file::move_to_file()
{
std::string tmp_dir;
if(temporary_dir_.empty()) {
char const *tmp=getenv("TEMP");
if(!tmp)
tmp=getenv("TMP");
if(!tmp)
tmp="/tmp";
tmp_dir=tmp;
}
else {
tmp_dir = temporary_dir_;
}
tmp_file_name_ = tmp_dir + "/cppcms_uploads_";
urandom_device rnd;
char buf[16];
char rand[33]={0};
rnd.generate(buf,16);
impl::tohex(buf,sizeof(buf),rand);
tmp_file_name_.append(rand);
tmp_file_name_+=".tmp";
file_.open(tmp_file_name_.c_str(),
std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::trunc);
if(!file_)
throw cppcms_error("Failed to create temporary file");
file_data_.seekg(0);
copy_stream(file_data_,file_);
file_data_.str("");
saved_in_file_ = 1;
}
void file::add_bytes_to_size(size_t n)
{
d->size += n;
}
file::file() :
size_limit_(1024*128),
saved_in_file_(0),
removed_(0),
d(new impl_data())
{
d->size = 0;
}
file::~file()
{
if(saved_in_file_ && !removed_) {
file_.close();
if(!tmp_file_name_.empty()) {
booster::nowide::remove(tmp_file_name_.c_str());
}
}
}
void file::filename(std::string const &v)
{
filename_=v;
}
void file::name(std::string const &v)
{
name_=v;
}
void file::mime(std::string const &v)
{
mime_=v;
}
} // http
} // cppcms