RetroShare/libretroshare/src/ft/ftextralist.cc

481 lines
9.3 KiB
C++
Raw Normal View History

/*
* libretroshare/src/ft: ftextralist.cc
*
* File Transfer for RetroShare.
*
* Copyright 2008 by Robert Fernie.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License Version 2 as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Please report all bugs and problems to "retroshare@lunamutt.com".
*
*/
This patch allows Windows users to share files and folders with "exotic" characters. The problem was that libretroshare handles files in UTF-8 but Windows's ANSI/POSIX C functions automatically assume that the char* parameters are encoded with the system's code page. There is no way to set that code page as UTF-8. So now under Windows the code translates the file name to UTF-16 before feeding it to one of the Unicode functions (they are usually prefixed or suffixed by 'w'). Please note that it is not very efficient. Furthermore, Windows does not provide a Unicode version of opendir/readdir/closedir, so it was necessary to use FindFirstFileW/FindNextFileW/FindClose which has a different behaviour as well as different structures. The FolderIterator class was created in order to mimic the Unix way of traversing folders contents. Hence the algorithm is unchanged and the systems differences masked. As it was necessary to use some functions from the Windows API, <windows.h> had to be included in a few files were it didn't appear before, creating macros and #define conflicts. In order to solve them, util/rswin.h must be included first in a file (if necessary). Otherwise the preprocessor will fail on purpose to avoid the code being compiled with different _WIN32_WINNT values. As another side-effect, rstlvutil.h and rstlvutil.cc have been removed from libretroshare.pro file. They are only used by testing units and include util/utest.h which defines a macro FAILED that already exists in <windows.h>. I don't know if unit tests are still in use and I don't plan on coding often on Windows, so I'll leave that as an exercise (hot potato?) to a motivated fellow Windows programmer. git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@2924 b45a01b8-16f6-495d-af2f-9b41ad6348cc
2010-05-16 19:26:45 -04:00
#ifdef WINDOWS_SYS
#include "util/rswin.h"
#endif
#include "ft/ftextralist.h"
#include "serialiser/rsconfigitems.h"
#include "util/rsdir.h"
#include <stdio.h>
/******
* #define DEBUG_ELIST 1
*****/
ftExtraList::ftExtraList()
:p3Config(CONFIG_TYPE_FT_EXTRA_LIST)
{
return;
}
void ftExtraList::run()
{
bool todo = false;
time_t cleanup = 0;
time_t now = 0;
while (isRunning())
{
#ifdef DEBUG_ELIST
//std::cerr << "ftExtraList::run() Iteration";
//std::cerr << std::endl;
#endif
now = time(NULL);
{
RsStackMutex stack(extMutex);
todo = (mToHash.size() > 0);
}
if (todo)
{
/* Hash a file */
hashAFile();
#ifdef WIN32
Sleep(1);
#else
/* microsleep */
usleep(10);
#endif
}
else
{
/* cleanup */
if (cleanup < now)
{
cleanupOldFiles();
cleanup = now + CLEANUP_PERIOD;
}
/* sleep */
#ifdef WIN32
Sleep(1000);
#else
sleep(1);
#endif
}
}
}
void ftExtraList::hashAFile()
{
#ifdef DEBUG_ELIST
std::cerr << "ftExtraList::hashAFile()";
std::cerr << std::endl;
#endif
/* extract entry from the queue */
FileDetails details;
{
RsStackMutex stack(extMutex);
if (mToHash.size() == 0)
return;
details = mToHash.front();
mToHash.pop_front();
}
#ifdef DEBUG_ELIST
std::cerr << "Hashing: " << details.info.path;
std::cerr << std::endl;
#endif
/* hash it! */
std::string name, hash;
//uint64_t size;
if (RsDirUtil::hashFile(details.info.path, details.info.fname,
details.info.hash, details.info.size))
{
RsStackMutex stack(extMutex);
/* stick it in the available queue */
mFiles[details.info.hash] = details;
/* add to the path->hash map */
mHashedList[details.info.path] = details.info.hash;
IndicateConfigChanged();
}
}
/***
* If the File is alreay Hashed, then just add it in.
**/
bool ftExtraList::addExtraFile(std::string path, std::string hash,
uint64_t size, uint32_t period, uint32_t flags)
{
#ifdef DEBUG_ELIST
std::cerr << "ftExtraList::addExtraFile() path: " << path;
std::cerr << " hash: " << hash;
std::cerr << " size: " << size;
std::cerr << " period: " << period;
std::cerr << " flags: " << flags;
std::cerr << std::endl;
#endif
RsStackMutex stack(extMutex);
FileDetails details;
details.info.path = path;
details.info.fname = RsDirUtil::getTopDir(path);
details.info.hash = hash;
details.info.size = size;
details.info.age = time(NULL) + period; /* if time > this... cleanup */
details.flags = flags;
/* stick it in the available queue */
mFiles[details.info.hash] = details;
IndicateConfigChanged();
return true;
}
bool ftExtraList::removeExtraFile(std::string hash, uint32_t flags)
{
#ifdef DEBUG_ELIST
std::cerr << "ftExtraList::removeExtraFile()";
std::cerr << " hash: " << hash;
std::cerr << " flags: " << flags;
std::cerr << std::endl;
#endif
RsStackMutex stack(extMutex);
std::map<std::string, FileDetails>::iterator it;
it = mFiles.find(hash);
if (it == mFiles.end())
{
return false;
}
mFiles.erase(it);
IndicateConfigChanged();
return true;
}
bool ftExtraList::moveExtraFile(std::string fname, std::string hash, uint64_t size,
std::string destpath)
{
RsStackMutex stack(extMutex);
std::map<std::string, FileDetails>::iterator it;
it = mFiles.find(hash);
if (it == mFiles.end())
{
return false;
}
std::string path = destpath + '/' + fname;
if (RsDirUtil::renameFile(it->second.info.path, path))
{
/* rename */
it->second.info.path = path;
it->second.info.fname = fname;
IndicateConfigChanged();
}
return true;
}
bool ftExtraList::cleanupOldFiles()
{
#ifdef DEBUG_ELIST
std::cerr << "ftExtraList::cleanupOldFiles()";
std::cerr << std::endl;
#endif
RsStackMutex stack(extMutex);
time_t now = time(NULL);
std::list<std::string> toRemove;
std::list<std::string>::iterator rit;
std::map<std::string, FileDetails>::iterator it;
for(it = mFiles.begin(); it != mFiles.end(); it++)
{
/* check timestamps */
if ((time_t)it->second.info.age < now)
{
toRemove.push_back(it->first);
}
}
if (toRemove.size() > 0)
{
/* remove items */
for(rit = toRemove.begin(); rit != toRemove.end(); rit++)
{
if (mFiles.end() != (it = mFiles.find(*rit)))
{
cleanupEntry(it->second.info.path, it->second.flags);
mFiles.erase(it);
}
}
IndicateConfigChanged();
}
return true;
}
bool ftExtraList::cleanupEntry(std::string path, uint32_t flags)
{
if (flags & RS_FILE_CONFIG_CLEANUP_DELETE)
{
/* Delete the file? - not yet! */
}
return true;
}
/***
* Hash file, and add to the files,
* file is removed after period.
**/
bool ftExtraList::hashExtraFile(std::string path, uint32_t period, uint32_t flags)
{
#ifdef DEBUG_ELIST
std::cerr << "ftExtraList::hashExtraFile() path: " << path;
std::cerr << " period: " << period;
std::cerr << " flags: " << flags;
std::cerr << std::endl;
#endif
/* add into queue */
RsStackMutex stack(extMutex);
FileDetails details(path, period, flags);
details.info.age = time(NULL) + period;
mToHash.push_back(details);
return true;
}
bool ftExtraList::hashExtraFileDone(std::string path, FileInfo &info)
{
#ifdef DEBUG_ELIST
std::cerr << "ftExtraList::hashExtraFileDone()";
std::cerr << std::endl;
#endif
std::string hash;
{
/* Find in the path->hash map */
RsStackMutex stack(extMutex);
std::map<std::string, std::string>::iterator it;
if (mHashedList.end() == (it = mHashedList.find(path)))
{
return false;
}
hash = it->second;
}
return search(hash, 0, info);
}
/***
* Search Function - used by File Transfer
*
**/
bool ftExtraList::search(const std::string &hash, uint32_t hintflags, FileInfo &info) const
{
#ifdef DEBUG_ELIST
std::cerr << "ftExtraList::search()";
std::cerr << std::endl;
#endif
/* find hash */
std::map<std::string, FileDetails>::const_iterator fit;
if (mFiles.end() == (fit = mFiles.find(hash)))
{
return false;
}
info = fit->second.info;
return true;
}
/***
* Configuration - store extra files.
*
**/
RsSerialiser *ftExtraList::setupSerialiser()
{
RsSerialiser *rss = new RsSerialiser();
/* add in the types we need! */
rss->addSerialType(new RsFileConfigSerialiser());
return rss;
}
bool ftExtraList::saveList(bool &cleanup, std::list<RsItem *>& sList)
{
cleanup = true;
/* called after each item is added */
/* create a list of fileitems with
* age used to specify its timeout.
*/
#ifdef DEBUG_ELIST
std::cerr << "ftExtraList::saveList()";
std::cerr << std::endl;
#endif
RsStackMutex stack(extMutex);
std::map<std::string, FileDetails>::const_iterator it;
for(it = mFiles.begin(); it != mFiles.end(); it++)
{
RsFileConfigItem *fi = new RsFileConfigItem();
fi->file.path = (it->second).info.path;
fi->file.name = (it->second).info.fname;
fi->file.hash = (it->second).info.hash;
fi->file.filesize = (it->second).info.size;
fi->file.age = (it->second).info.age;
fi->flags = (it->second).flags;
sList.push_back(fi);
}
return true;
}
bool ftExtraList::loadList(std::list<RsItem *>& load)
{
/* for each item, check it exists ....
* - remove any that are dead (or flag?)
*/
#ifdef DEBUG_ELIST
std::cerr << "ftExtraList::loadList()";
std::cerr << std::endl;
#endif
time_t ts = time(NULL);
std::list<RsItem *>::iterator it;
for(it = load.begin(); it != load.end(); it++)
{
RsFileConfigItem *fi = dynamic_cast<RsFileConfigItem *>(*it);
if (!fi)
{
delete (*it);
continue;
}
/* open file */
FILE *fd = RsDirUtil::rs_fopen(fi->file.path.c_str(), "rb");
if (fd == NULL)
{
delete (*it);
continue;
}
fclose(fd);
fd = NULL ;
if (ts > (time_t)fi->file.age)
{
/* to old */
cleanupEntry(fi->file.path, fi->flags);
delete (*it);
continue ;
}
/* add into system */
FileDetails file;
RsStackMutex stack(extMutex);
FileDetails details;
details.info.path = fi->file.path;
details.info.fname = fi->file.name;
details.info.hash = fi->file.hash;
details.info.size = fi->file.filesize;
details.info.age = fi->file.age; /* time that we remove it. */
details.flags = fi->flags;
/* stick it in the available queue */
mFiles[details.info.hash] = details;
delete (*it);
/* short sleep */
#ifndef WINDOWS_SYS
/********************************** WINDOWS/UNIX SPECIFIC PART ******************/
usleep(1000); /* 1000 per second */
#else
/********************************** WINDOWS/UNIX SPECIFIC PART ******************/
Sleep(1);
#endif
/********************************** WINDOWS/UNIX SPECIFIC PART ******************/
}
return true;
}