winix/winixd/functions/cp.cpp

450 lines
9.2 KiB
C++

/*
* This file is a part of Winix
* and is distributed under the 2-Clause BSD licence.
* Author: Tomasz Sowa <t.sowa@ttmath.org>
*/
/*
* Copyright (c) 2008-2021, Tomasz Sowa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "cp.h"
#include "core/misc.h"
#include "functions.h"
namespace Winix
{
namespace Fun
{
Cp::Cp()
{
fun.url = L"cp";
}
bool Cp::HasAccess()
{
return CheckAccessFrom();
}
bool Cp::CheckAccessFrom()
{
if( cur->request->is_item )
{
if( !system->HasReadAccess(cur->request->item) || cur->request->item.type == Item::symlink )
{
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
return false;
}
}
else
if( !cur->request->IsParam(L"r") )
{
// directories need 'r' parameter
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
}
return true;
}
bool Cp::CheckAccessTo()
{
if( dir_tab.empty() ||
!system->HasReadExecAccessToPath(dir_tab) ||
!system->HasWriteAccess(*dir_tab.back()) )
{
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
return false;
}
return true;
}
bool Cp::ParseDir()
{
const std::wstring & new_dir = cur->request->PostVar(L"to");
int res = system->dirs.FollowLink(cur->request->dir_tab, new_dir, dir_tab, file);
if( res == 3 )
cur->request->status = WINIX_ERR_NO_ROOT_DIR;
else
if( res != 0 && res != 1 )
cur->request->status = WINIX_ERR_INCORRECT_DIR;
return res == 0 || res == 1;
}
bool Cp::CopyStaticFile(const std::wstring & from, const std::wstring & to)
{
if( from == to )
{
log << log3 << "Cp: the same path to a static file: " << to << logend;
return true;
}
if( Winix::CopyFile(from, to) )
{
log << log2 << "Cp: copied a static file from: " << from << ", to: " << to << logend;
return true;
}
else
{
log << log1 << "Cp: can't copy a file from: " << from << ", to: " << to << logend;
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
return false;
}
}
void Cp::CopyStaticFile(Item & item)
{
bool ok = true;
ok = ok && system->MakeFilePath(item, old_path, false);
ok = ok && (!item.item_content.file_has_thumb || system->MakeFilePath(item, old_path_thumb, true));
ok = ok && system->CreateNewFile(item);
ok = ok && system->MakeFilePath(item, new_path, false, true, config->upload_dirs_chmod);
ok = ok && (!item.item_content.file_has_thumb || system->MakeFilePath(item, new_path_thumb, true, true, config->upload_dirs_chmod));
if( !ok )
{
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
return;
}
if( CopyStaticFile(old_path, new_path) )
{
//cur->request->status = db->EditFileById(item, item.id);
item.propagate_connector();
if( !item.item_content.update() )
{
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
}
if( item.item_content.file_has_thumb )
CopyStaticFile(old_path_thumb, new_path_thumb);
}
}
void Cp::SetNewAttributes(Item & item)
{
item.item_content.user_id = new_user;
item.item_content.group_id = new_group;
item.item_content.SetDateModifyToNow();
}
void Cp::CopyFile(Item & item, long dst_dir_id)
{
if( !preserve_attr )
SetNewAttributes(item);
item.parent_id = dst_dir_id;
// cur->request->status = db->AddItem(item);
// if( cur->request->status == WINIX_ERR_OK )
if( item.insert() )
{
if( item.item_content.file_type != WINIX_ITEM_FILETYPE_NONE )
CopyStaticFile(item);
plugin->Call(WINIX_FILE_COPIED, &item);
}
}
void Cp::CopyFileOrSymlink(Item & item, long dst_dir_id)
{
if( !system->HasReadAccess(item) )
return; // !! w przyszlosci bedziemy dodawac komunikaty do specjalnej tablicy (narazie nie zaimplementowane)
if( item.type == Item::symlink && follow_symlinks )
{
if( system->dirs.CreateDirTab(item.parent_id, symlink_dir_tab) )
{
int res = system->FollowAllLinks(symlink_dir_tab, item.item_content.link_to, symlink_dir_tab, item);
if( res == 0 )
CopyDirTree(*symlink_dir_tab.back(), dst_dir_id);
else
if( res == 1 )
CopyFile(item, dst_dir_id);
}
}
else
{
CopyFile(item, dst_dir_id);
}
}
void Cp::Prepare()
{
// iq.SetAll(true, false);
// iq.WhereType(Item::dir, false);
new_user = -1;
new_group = dir_tab.back()->item_content.group_id;
if( cur->session->puser )
new_user = cur->session->puser->id;
loop_checker.clear();
}
void Cp::CopyFilesInDir(const Item & dir, long dst_dir_id)
{
morm::Finder<Item> finder(model_connector);
item_tab = finder.
select().
where().
neq(L"type", static_cast<int>(Item::dir)).
eq(L"parent_id", dir.id).
get_vector();
//iq.WhereParentId(dir.id);
//db->GetItems(item_tab, iq);
for(size_t i=0 ; i<item_tab.size() ; ++i)
CopyFileOrSymlink(item_tab[i], dst_dir_id);
}
void Cp::CopyDirContentTree(const Item & dir, long dst_dir_id)
{
DirContainer::ParentIterator i = system->dirs.FindFirstChild(dir.id);
// go through all directories
for( ; i != system->dirs.ParentEnd() ; i = system->dirs.NextChild(i) )
CopyDirTree(*(i->second), dst_dir_id);
CopyFilesInDir(dir, dst_dir_id);
}
bool Cp::WasThisDir(const Item & dir)
{
for(size_t i=0 ; i<loop_checker.size() ; ++i)
if( loop_checker[i] == dir.id )
return true;
return false;
}
// we shouldn't change 'item' because we have references to our app.dirs objects
long Cp::CopyDirTree(const Item & dir, long dst_dir_id)
{
if( WasThisDir(dir) )
{
log << log1 << "Cp: a loop between directories found (created by a symlink), "
<< "dir_id: " << dir.id << ", dir_url: " << dir.url << logend;
return -1;
}
loop_checker.push_back(dir.id);
temp = dir;
temp.parent_id = dst_dir_id;
if( !file.empty() )
{
temp.url = file;
functions->PrepareUrl(temp);
file.clear();
}
if( !preserve_attr )
SetNewAttributes(temp);
if( remove_defaults )
{
temp.item_content.link_to.clear();
temp.item_content.link_redirect = 0;
}
bool status = system->dirs.AddDirectory(temp);
if( !status )
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
loop_checker.push_back(temp.id);
// remember the new dir_id because temp can be changed
// this method is called in recurrences
long new_dir_id = temp.id;
if( system->HasReadExecAccess(dir) )
CopyDirContentTree(dir, temp.id);
return new_dir_id; // and return it
}
bool Cp::IsTheSameFile(const Item & item)
{
if( file.empty() )
{
if( item.parent_id == dir_tab.back()->id )
return true; // nothing to do
}
else
{
if( item.parent_id == dir_tab.back()->id && item.url == file )
return true; // nothing to do
}
return false;
}
// here 'item' can be changed in place
void Cp::PostCopyFile(Item & item, bool redirect)
{
if( IsTheSameFile(item) )
return;
if( !file.empty() )
{
item.url = file;
functions->PrepareUrl(item);
file.clear();
}
CopyFileOrSymlink(item, dir_tab.back()->id);
if( cur->request->status == WINIX_ERR_OK && redirect )
system->RedirectTo(item);
}
void Cp::PostCopyDirContent(const Item & dir, bool redirect)
{
if( !file.empty() )
{
cur->request->status = WINIX_ERR_INCORRECT_DIR;
return;
}
if( dir_tab.back()->id == dir.id )
return; // nothing to do
CopyDirContentTree(dir, dir_tab.back()->id);
if( cur->request->status == WINIX_ERR_OK && redirect )
system->RedirectTo(dir_tab.back()->id);
}
void Cp::PostCopyDir(const Item & dir, bool redirect)
{
long dir_id = dir_tab.back()->id;
if( file.empty() && dir_id == dir.id )
return; // nothing to do
long new_dir_id = CopyDirTree(dir, dir_id);
if( new_dir_id != -1 && cur->request->status == WINIX_ERR_OK && redirect )
system->RedirectTo(new_dir_id);
}
void Cp::Clear()
{
loop_checker.clear();
dir_tab.clear();
item_tab.clear();
symlink_dir_tab.clear();
}
void Cp::MakePost()
{
if( ParseDir() && CheckAccessTo() )
{
Prepare();
preserve_attr = cur->request->IsPostVar(L"preserveattr");
remove_defaults = cur->request->IsPostVar(L"removedefaults");
follow_symlinks = cur->request->IsPostVar(L"followsymlinks");
if( cur->request->is_item )
{
PostCopyFile(cur->request->item);
}
else
{
if( cur->request->IsPostVar(L"onlycontent") )
PostCopyDirContent(*cur->request->dir_tab.back());
else
PostCopyDir(*cur->request->dir_tab.back());
}
Clear();
}
}
} // namespace
} // namespace Winix