Files
winix/winixd/functions/rm.cpp
Tomasz Sowa d5ebb7ca12 changed the way how the request's answer is created,
now winix can return json, xml, csv from out_main_stream or from frames and json from models

removed from Request:
  bool send_bin_stream
  bool return_json
  bool return_info_only
  pt::Space info
  bool page_generated
  bool out_main_stream_use_html_filter
  bool out_streams_use_html_filter

added to Request:
  enum AnswerSource
  enum AnswerContainer
  AnswerSource answer_source
  AnswerContainer answer_container
  bool use_ezc_engine
  std::wstring frame
  bool send_all_frames
  bool use_html_filter

added to Config:
 // the name of the url parameter for returning all frames, e.g. https://domain.tld/mydir/myfunction/allframes
 // default: allframes
 std::wstring request_all_frames_parameter;

 // the name of the root element when serializing request answer to xml
 // default: winix
 std::wstring xml_root;

algorithm (the whole algorithm is described in core/request.h):
at the beginning of a request winix sets
   answer_source to models
   answer_container to text
   use_ezc_engine to true

next answer_container and use_ezc_engine can be changed in the following way:

1. winix will look for 'Accept' http header and depending on the header winix will set:
   (not implemented yet)

             Accept | answer_container | use_ezc_engine
	     ------------------------------------|-----------------
	     application/json | json             | false
	      application/xml | xml              | false
	              text/csv | csv              | false

2. next answer_container is set depending on 'container' url parameter
       container | answer_container
 ---------------------------------------------------------
     not present | don't change the value
            text | text
            json | json
             xml | xml
             csv | csv

use_ezc_engine is set depending on 'answer' url parameter:
          answer | use_ezc_engine
 ---------------------------------
     not present | don't change the value
            html | true
            data | false

if 'answer' is html then we take into account two more parameters:
 frame: frame_name (empty default) - if set then winix returns this specific frame
 allframes: (if present then winix returns all frames)
2021-10-13 01:27:14 +02:00

567 lines
13 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 <cstdio>
#include "rm.h"
#include "core/misc.h"
#include "templates/templates.h"
namespace Winix
{
namespace Fun
{
Rm::Rm()
{
fun.url = L"rm";
follow_symlinks = false;
}
bool Rm::HasAccessToDir(const Item & dir, bool only_content)
{
if( dir.parent_id == -1 )
{
// this is a root directory
// we can only remove the content of the root directory
// sticky bit for a specified child will be checked later
if( !only_content || !system->HasWriteAccess(dir) )
return false;
}
else
if( only_content )
{
// sticky bit for a specified child will be checked later
if( !system->HasWriteAccess(dir) )
return false;
}
else
{
Item * last_but_one_dir = system->dirs.GetDir(dir.parent_id);
if( !last_but_one_dir )
// ops, there is no a parent dir
return false;
if( !system->CanRemoveRenameChild(*last_but_one_dir, dir.item_content.user_id) )
return false;
}
return true;
}
/*
here we are making a little test -- this method returns false if
we are sure that the permissions is not allowed (consequently the html form
will not be displayed)
the correct checking for permissions is done later in MakePost() method
parameter 'c' to rm function means removing only the content of a directory
this parameter can be sent either by a get or a post variable
*/
bool Rm::HasAccess()
{
bool res = false;
if( cur->request->is_item )
{
res = system->CanRemoveRenameChild(*cur->request->dir_tab.back(), cur->request->item.item_content.user_id);
}
else
{
if( cur->request->IsParam(L"r") )
{
bool only_content = (cur->request->IsParam(L"c") || cur->request->IsPostVar(L"c"));
res = HasAccessToDir(*cur->request->dir_tab.back(), only_content);
}
else
{
log << log3 << "Rm: directories can be removed only with 'r' parameter" << logend;
slog << logerror << T("rm_use_r_option") << logend;
}
}
if( !res && cur->request->IsParam(L"jquery_upload") )
CreateJSON(res);
return res;
}
void Rm::Prepare()
{
// selecting files and symlinks (without directories)
// content_dir_iq.SetAll(false, false);
// content_dir_iq.sel_parent_id = true;
// content_dir_iq.sel_type = true;
// content_dir_iq.sel_url = true;
// content_dir_iq.sel_file = true;
// content_dir_iq.sel_user_id = true;
// content_dir_iq.sel_group_id = true;
// content_dir_iq.sel_privileges = true;
// content_dir_iq.sel_meta = true;
// content_dir_iq.WhereType(Item::dir, false);
}
void Rm::RemoveStaticFile(const std::wstring & path)
{
if( Winix::RemoveFile(path) )
{
log << log2 << "Rm: static file removed: " << path << logend;
}
else
{
log << log1 << "Rm: I can't remove a static file: " << path << logend;
slog << logerror << T("rm_cannot_remove_static_file") << ": " << path << logend;
}
}
void Rm::RemoveStaticFile(const Item & item)
{
if( system->MakeFilePath(item, path, false) )
{
RemoveStaticFile(path);
if( item.item_content.file_has_thumb && system->MakeFilePath(item, path, true) )
RemoveStaticFile(path);
}
else
{
log << log1 << "Rm: I cannot create a path to a static file, url: "
<< item.url << ", id: " << item.id << logend;
slog << logerror << T("rm_cannot_create_static_path") << ": " << item.url << logend;
}
}
/*
!! IMPROVE ME:
plugin.Call(WINIX_FILE_REMOVED, &item) is getting its parameter as non-const
in the future we can add a pointer cp1, cp2 (in plugins) but pointing to a const object
and this bool Rm::RemoveFile(Item & item) can became bool Rm::RemoveFile(const Item & item)
*/
bool Rm::RemoveFile(Item & item)
{
plugin->Call(WINIX_FILE_PREPARE_TO_REMOVE, &item);
//if( db->DelItem(item) == WINIX_ERR_OK )
if( item.remove() )
{
if( item.type == Item::file )
log << log2 << "Rm: deleted file: ";
else
log << log2 << "Rm: deleted symlink: ";
log << item.url << logend;
TemplatesFunctions::pattern_cacher.DeletePattern(item);
if( item.item_content.file_type != WINIX_ITEM_FILETYPE_NONE )
RemoveStaticFile(item);
plugin->Call(WINIX_FILE_REMOVED, &item);
return true;
}
return false;
}
/*
we do not modify item
it is non-const because we use plugin.Call() later with a pointer to this structure
(and we want to avoid casting)
*/
bool Rm::RemoveFileOrSymlink(Item & item, bool check_access)
{
if( item.type == Item::dir )
return false;
if( check_access )
{
Item * dir = system->dirs.GetDir(item.parent_id);
// if there is not 'dir' directory then we can simply remove 'item'
if( dir )
{
if( !system->CanRemoveRenameChild(*dir, item.item_content.user_id) )
{
log << log1 << "Rm: permission denied to remove: " << item.url << ", id: " << item.id << logend;
slog << logerror << T("rm_permission_denied_to") << ": " << item.url << logend;
return false;
}
}
}
return RemoveFile(item);
}
bool Rm::RemoveFileOrSymlink(long item_id, bool check_access)
{
bool result = false;
// selecting files, symlinks and directories
// rm_by_id_iq.SetAll(false, false);
// rm_by_id_iq.sel_parent_id = true;
// rm_by_id_iq.sel_type = true;
// rm_by_id_iq.sel_url = true;
// rm_by_id_iq.sel_file = true;
// rm_by_id_iq.sel_user_id = true;
// rm_by_id_iq.sel_group_id = true;
// rm_by_id_iq.sel_privileges = true;
// rm_by_id_iq.sel_meta = true;
// rm_by_id_iq.WhereId(item_id);
morm::Finder<Item> finder(model_connector);
rm_by_id_item = finder.
select().
where().
eq(L"id", item_id).
get();
//if( db->GetItem(rm_by_id_item, rm_by_id_iq) == WINIX_ERR_OK )
if( rm_by_id_item.found() )
{
if( rm_by_id_item.type == Item::file || rm_by_id_item.type == Item::symlink )
{
result = RemoveFileOrSymlink(rm_by_id_item, check_access);
}
else
{
log << log2 << "Rm: I cannot remove file or symlink, item_id: " << item_id
<< " is a directory" << logend;
}
}
return result;
}
// for other uses (plugins etc)
bool Rm::RemoveItemById(long item_id, bool check_access)
{
bool result = false;
// selecting files, symlinks and directories
// rm_by_id_iq.SetAll(false, false);
// rm_by_id_iq.sel_parent_id = true;
// rm_by_id_iq.sel_type = true;
// rm_by_id_iq.sel_url = true;
// rm_by_id_iq.sel_file = true;
// rm_by_id_iq.sel_user_id = true;
// rm_by_id_iq.sel_group_id = true;
// rm_by_id_iq.sel_privileges = true;
// rm_by_id_iq.sel_meta = true;
// rm_by_id_iq.WhereId(item_id);
morm::Finder<Item> finder(model_connector);
rm_by_id_item = finder.
select().
where().
eq(L"id", item_id).
get();
//if( db->GetItem(rm_by_id_item, rm_by_id_iq) == WINIX_ERR_OK )
if( rm_by_id_item.found() )
{
if( rm_by_id_item.type == Item::dir )
{
RemoveDirTree(rm_by_id_item, true, check_access);
result = true; // RemoveDirTree doesn't return a status
}
else
{
result = RemoveFileOrSymlink(rm_by_id_item, check_access);
}
}
return result;
}
bool Rm::RemoveItemByPath(const std::wstring & path, bool check_access)
{
bool result = false;
int res = system->FollowAllLinks(path, rm_path_dir_tab, rm_path_item);
if( res == 0 )
{
Item * dir = system->dirs.GetDir(rm_path_dir_tab.back()->id);
if( dir )
{
RemoveDirTree(*dir, true, check_access);
result = false;
}
}
else
if( res == 1 )
{
result = RemoveFileOrSymlink(rm_path_item, check_access);
}
return result;
}
bool Rm::RemoveDirFiles(long dir_id, bool check_access)
{
// content_dir_iq.WhereParentId(dir_id);
// db->GetItems(content_item_tab, content_dir_iq);
morm::Finder<Item> finder(model_connector);
finder.
select().
where().
eq(L"parent_id", dir_id).
get_vector(content_item_tab);
size_t removed = 0;
for(size_t i=0 ; i<content_item_tab.size() ; ++i)
if( RemoveFileOrSymlink(content_item_tab[i], check_access) )
removed += 1;
return removed == content_item_tab.size();
}
/*
parent_dir can be null (when current_dir is the root directory)
*/
void Rm::RemoveCurrentDir(Item * parent_dir, Item * current_dir, bool check_access)
{
if( check_access )
{
if( !parent_dir || !system->CanRemoveRenameChild(*parent_dir, current_dir->item_content.user_id) )
{
log << log1 << "Rm: permission denied to directory: " << current_dir->url << logend;
slog << logerror << T("rm_permission_denied_to") << ": " << current_dir->url << logend;
return;
}
}
plugin->Call(WINIX_DIR_PREPARE_TO_REMOVE, current_dir);
//if( db->DelDirById(current_dir->id) == WINIX_ERR_OK )
if( current_dir->remove() )
{
long dir_id = current_dir->id;
old_url = current_dir->url;
system->dirs.DelDir(dir_id);
// don't use current_dir pointer anymore
log << log2 << "Rm: directory removed: " << old_url << logend;
plugin->Call(WINIX_DIR_REMOVED, dir_id);
}
else
{
log << log1 << "Rm: I cannot remove a directory: " << current_dir->url << " (database error)" << logend;
}
}
/*
parent_dir can be null (when current_dir is pointing to the root directory)
current_dir is pointing to the directory which should be deleted
(contents of this directory is removed but the directory is deleted only if remove_this_dir is true)
*/
void Rm::RemoveDirTree(Item * parent_dir, Item * current_dir, bool remove_this_dir, bool check_access)
{
if( check_access && !system->HasReadExecAccess(*current_dir) )
{
log << log1 << "Rm: permission denied to directory: " << current_dir->url << logend;
slog << logerror << T("rm_permission_denied_to") << ": " << current_dir->url << logend;
return;
}
DirContainer::ParentIterator pnext, p = system->dirs.FindFirstChild(current_dir->id);
for( ; p != system->dirs.ParentEnd() ; p = pnext )
{
Item * child_dir = &(*p->second);
// this iterator p will be invalidated by the below RemoveDirTree() call
// (the next iterator we must calculate beforehand)
pnext = system->dirs.NextChild(p);
RemoveDirTree(current_dir, child_dir, true, check_access);
}
bool all_file_removed = RemoveDirFiles(current_dir->id, check_access);
if( remove_this_dir )
{
if( all_file_removed )
{
RemoveCurrentDir(parent_dir, current_dir, check_access);
// don't use current_dir pointer anymore
}
else
{
log << log1 << "Rm: " << current_dir->url << " directory not empty" << logend;
slog << logerror << current_dir->url << T("rm_directory_not_empty") << logend;
}
}
}
void Rm::RemoveDirTree(Item & dir, bool remove_this_dir, bool check_access)
{
Item * parent = system->dirs.GetDir(dir.parent_id);
RemoveDirTree(parent, &dir, remove_this_dir, check_access);
}
void Rm::RemoveDirContent()
{
if( !cur->request->IsParam(L"r") )
{
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
log << log3 << "Rm: directory content can be removed only with 'r' parameter" << logend;
slog << logerror << T("rm_content_use_r_option") << logend;
return;
}
RemoveDirTree(*cur->request->dir_tab.back(), false, true);
}
void Rm::RemoveDir()
{
if( !cur->request->IsParam(L"r") )
{
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
log << log3 << "Rm: a directory can be removed only with 'r' parameter" << logend;
slog << logerror << T("rm_use_r_option") << logend;
return;
}
if( cur->request->dir_tab.size() <= 1 )
{
cur->request->status = WINIX_ERR_PERMISSION_DENIED;
log << log1 << "Rm: the root directory cannot be removed" << logend;
slog << logerror << T("rm_cannot_remove_root_dir") << logend;
return;
}
RemoveDirTree(*cur->request->dir_tab.back(), true, true);
cur->request->dir_tab.erase(--cur->request->dir_tab.end());
}
void Rm::Clear()
{
content_item_tab.clear();
files.clear();
}
/*
*
*/
void Rm::CreateJSON(bool status)
{
pt::Space & file = files.add_empty_space();
file.add(cur->request->item.url, status);
cur->request->models.Add(L"files", files);
}
void Rm::MakePost()
{
Prepare();
if( cur->request->is_item )
{
RemoveFileOrSymlink(cur->request->item, true);
}
else
{
if( cur->request->IsParam(L"c") || cur->request->IsPostVar(L"c") )
RemoveDirContent();
else
RemoveDir();
}
Clear();
if( cur->request->IsParam(L"jquery_upload") )
CreateJSON(true);
else
if( cur->request->status == WINIX_ERR_OK )
system->RedirectToLastDir();
}
} // namespace
} // namespace Winix