/******************************************************************************* * libretroshare/src/file_sharing: file_tree.cc * * * * libretroshare: retroshare core library * * * * Copyright (C) 2018 Retroshare Team * * Copyright (C) 2020 Gioacchino Mazzurco * * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 3 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public License * * along with this program. If not, see . * * * ******************************************************************************/ #include #include "util/radix64.h" #include "util/rsbase64.h" #include "util/rsdir.h" #include "retroshare/rsfiles.h" #include "file_sharing_defaults.h" #include "filelist_io.h" #include "serialiser/rstypeserializer.h" void RsFileTree::DirData::serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) { RS_SERIAL_PROCESS(name); RS_SERIAL_PROCESS(subdirs); RS_SERIAL_PROCESS(subfiles); } void RsFileTree::FileData::serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) { RS_SERIAL_PROCESS(name); RS_SERIAL_PROCESS(size); RS_SERIAL_PROCESS(hash); } /*static*/ std::tuple, std::error_condition> RsFileTree::fromBase64(const std::string& base64) { const auto failure = [](std::error_condition ec) { return std::make_tuple(nullptr, ec); }; if(base64.empty()) return failure(std::errc::invalid_argument); std::error_condition ec; std::vector mem; if( (ec = RsBase64::decode(base64, mem)) ) return failure(ec); RsGenericSerializer::SerializeContext ctx( mem.data(), static_cast(mem.size()), RsSerializationFlags::INTEGER_VLQ ); std::unique_ptr ft(new RsFileTree); ft->serial_process( RsGenericSerializer::SerializeJob::DESERIALIZE, ctx); if(ctx.mOk) return std::make_tuple(std::move(ft), std::error_condition()); return failure(std::errc::invalid_argument); } std::string RsFileTree::toBase64() const { RsGenericSerializer::SerializeContext ctx; ctx.mFlags = RsSerializationFlags::INTEGER_VLQ; RsFileTree* ncThis = const_cast(this); ncThis->serial_process( RsGenericSerializer::SerializeJob::SIZE_ESTIMATE, ctx ); std::vector buf(ctx.mOffset); ctx.mSize = ctx.mOffset; ctx.mOffset = 0; ctx.mData = buf.data(); ncThis->serial_process( RsGenericSerializer::SerializeJob::SERIALIZE, ctx ); std::string result; RsBase64::encode(ctx.mData, ctx.mSize, result, false, true); return result; } std::string RsFileTree::toRadix64() const { unsigned char* buff = nullptr; uint32_t size = 0; serialise(buff, size); std::string res; Radix64::encode(buff,size,res); free(buff); return res; } std::unique_ptr RsFileTree::fromRadix64( const std::string& radix64_string ) { std::unique_ptr ft(new RsFileTree); std::vector mem = Radix64::decode(radix64_string); if(ft->deserialise(mem.data(), static_cast(mem.size()))) return ft; return nullptr; } void RsFileTree::recurs_buildFileTree( RsFileTree& ft, uint32_t index, const DirDetails& dd, bool remote, bool remove_top_dirs ) { RsDbg() << __PRETTY_FUNCTION__ << " index: " << index << std::endl; if(ft.mDirs.size() <= index) ft.mDirs.resize(index+1) ; if(remove_top_dirs) ft.mDirs[index].name = RsDirUtil::getTopDir(dd.name) ; else ft.mDirs[index].name = dd.name ; ft.mDirs[index].subfiles.clear(); ft.mDirs[index].subdirs.clear(); DirDetails dd2 ; FileSearchFlags flags = remote ? RS_FILE_HINTS_REMOTE : RS_FILE_HINTS_LOCAL; for(uint32_t i=0;iRequestDirDetails(dd.children[i].ref,dd2,flags)) { if(dd.children[i].type == DIR_TYPE_FILE) { FileData f ; f.name = dd2.name ; f.size = dd2.size ; f.hash = dd2.hash ; ft.mDirs[index].subfiles.push_back(ft.mFiles.size()) ; ft.mFiles.push_back(f) ; ft.mTotalFiles++ ; ft.mTotalSize += f.size ; } else if(dd.children[i].type == DIR_TYPE_DIR) { ft.mDirs[index].subdirs.push_back(ft.mDirs.size()); recurs_buildFileTree(ft,ft.mDirs.size(),dd2,remote,remove_top_dirs) ; } else std::cerr << "(EE) Unsupported DirDetails type." << std::endl; } else std::cerr << "(EE) Cannot request dir details for pointer " << dd.children[i].ref << std::endl; } bool RsFileTree::getDirectoryContent( std::string& name, std::vector& subdirs, std::vector& subfiles, uint64_t index_p ) const { // Avoid warnings on Android armv7 using sz_t = std::vector::size_type; sz_t index = static_cast(index_p); if(index >= mDirs.size()) return false; name = mDirs[index].name; subdirs = mDirs[index].subdirs ; subfiles.clear() ; for(sz_t i=0; i < mDirs[index].subfiles.size(); ++i) subfiles.push_back(mFiles[static_cast(mDirs[index].subfiles[i])]); return true; } std::unique_ptr RsFileTree::fromDirDetails( const DirDetails& dd, bool remote ,bool remove_top_dirs ) { std::unique_ptrft(new RsFileTree); if(dd.type == DIR_TYPE_FILE) { FileData fd; fd.name = dd.name; fd.hash = dd.hash; fd.size = dd.size; ft->mFiles.push_back(fd); ft->mTotalFiles = 1; ft->mTotalSize = fd.size; DirData dd; dd.name = "/"; dd.subfiles.push_back(0); ft->mDirs.push_back(dd); } else recurs_buildFileTree(*ft, 0, dd, remote, remove_top_dirs ); return ft; } typedef FileListIO::read_error read_error ; bool RsFileTree::deserialise(unsigned char *buffer,uint32_t buffer_size) { uint32_t buffer_offset = 0 ; mTotalFiles = 0; mTotalSize = 0; try { // Read some header uint32_t version,n_dirs,n_files ; if(!FileListIO::readField(buffer,buffer_size,buffer_offset,FILE_LIST_IO_TAG_LOCAL_DIRECTORY_VERSION,version)) throw read_error(buffer,buffer_size,buffer_offset,FILE_LIST_IO_TAG_LOCAL_DIRECTORY_VERSION) ; if(version != (uint32_t) FILE_LIST_IO_LOCAL_DIRECTORY_TREE_VERSION_0001) throw std::runtime_error("Wrong version number") ; if(!FileListIO::readField(buffer,buffer_size,buffer_offset,FILE_LIST_IO_TAG_RAW_NUMBER,n_files)) throw read_error(buffer,buffer_size,buffer_offset,FILE_LIST_IO_TAG_RAW_NUMBER) ; if(!FileListIO::readField(buffer,buffer_size,buffer_offset,FILE_LIST_IO_TAG_RAW_NUMBER,n_dirs)) throw read_error(buffer,buffer_size,buffer_offset,FILE_LIST_IO_TAG_RAW_NUMBER) ; // Write all file/dir entries mFiles.resize(n_files) ; mDirs.resize(n_dirs) ; unsigned char *node_section_data = NULL ; uint32_t node_section_size = 0 ; for(uint32_t i=0;i