mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-05-19 14:30:43 -04:00

git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@246 b45a01b8-16f6-495d-af2f-9b41ad6348cc
1220 lines
30 KiB
C++
1220 lines
30 KiB
C++
/*
|
|
* "$Id: pqifiler.cc,v 1.13 2007-02-19 20:08:30 rmf24 Exp $"
|
|
*
|
|
* Other Bits for RetroShare.
|
|
*
|
|
* Copyright 2004-2006 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".
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "server/pqifiler.h"
|
|
|
|
#include "pqi/pqidebug.h"
|
|
#include <errno.h>
|
|
|
|
#include <sstream>
|
|
|
|
const int pqifilerzone = 86539;
|
|
|
|
/*
|
|
* PQI Filer
|
|
*
|
|
* This managers the file transfers.
|
|
*
|
|
*
|
|
* TODO: add trickle transfers.
|
|
*
|
|
*/
|
|
|
|
const int PQIFILE_OFFLINE_CHECK = 120; /* check every 2 minutes */
|
|
const int PQIFILE_DOWNLOAD_TIMEOUT = 60; /* time it out, -> offline after 60 secs */
|
|
const int PQIFILE_DOWNLOAD_CHECK = 10; /* desired delta = 10 secs */
|
|
const int PQIFILE_DOWNLOAD_TOO_FAST = 8; /* 8 secs */
|
|
const int PQIFILE_DOWNLOAD_TOO_SLOW = 12; /* 12 secs */
|
|
const int PQIFILE_DOWNLOAD_MIN_DELTA = 5; /* 5 secs */
|
|
|
|
const float TRANSFER_MODE_TRICKLE_RATE = 1000; /* 1 kbyte limit */
|
|
const float TRANSFER_MODE_NORMAL_RATE = 500000; /* 500 kbyte limit - everyone uses this one for now */
|
|
const float TRANSFER_MODE_FAST_RATE = 500000; /* 500 kbyte limit */
|
|
|
|
const int TRANSFER_START_MIN = 500; /* 500 byte min limit */
|
|
const int TRANSFER_START_MAX = 2000; /* 2000 byte max limit */
|
|
|
|
#ifdef USE_FILELOOK
|
|
pqifiler::pqifiler(fileLook *fd)
|
|
:fileIndex(fd) { return; }
|
|
#else
|
|
pqifiler::pqifiler(filedex *fd)
|
|
:fileIndex(fd) { return; }
|
|
#endif
|
|
|
|
|
|
/************* Local File Interface ****************************
|
|
*
|
|
* int pqifiler::getFile(PQFileItem *in);
|
|
* int pqifiler::cancelFile(PQFileItem *i);
|
|
* int pqifiler::clearFailedTransfers();
|
|
*
|
|
* * Worker Fns.
|
|
* PQFileStatus *pqifiler::findRecvFileItem(PQFileItem *in);
|
|
*/
|
|
|
|
int pqifiler::getFile(PQFileItem *in)
|
|
{
|
|
/* add to local queue */
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::getFile(): " << std::endl;
|
|
in -> print(out);
|
|
out << std::endl;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
}
|
|
|
|
|
|
|
|
/* check for duplicates */
|
|
PQFileStatus *state = findRecvFileItem(in);
|
|
if (state)
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::getFile() - duplicate, giving push!";
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
|
|
/* if duplicate - give download a push! */
|
|
/* do this by flagging last transfer at 0.
|
|
*/
|
|
/* and also set the request stuff, so it'll
|
|
* generate a new request
|
|
* - we might lose the current transfer - but
|
|
* that's the idiots fault for redownloading.
|
|
*/
|
|
|
|
resetFileTransfer(state);
|
|
return 1;
|
|
}
|
|
|
|
state = new PQFileStatus(in);
|
|
if (initiateFileTransfer(state))
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::getFile() ";
|
|
out << "adding to recvFiles queue";
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
|
|
recvFiles.push_back(state);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
PQFileStatus *pqifiler::findRecvFileItem(PQFileItem *in)
|
|
{
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::findRecvFileItem() Looking for: " << in->name;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
}
|
|
|
|
/* iterate through fileItems and check for this one */
|
|
std::list<PQFileStatus *>::iterator it;
|
|
for(it = recvFiles.begin(); it != recvFiles.end(); it++)
|
|
{
|
|
if ((in->name==(*it)->fileItem->name) &&
|
|
(in->hash==(*it)->fileItem->hash))
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::findRecvFileItem() ";
|
|
out << "Match PQFileStatus: " << in -> name;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
/* same */
|
|
return (*it);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
int pqifiler::cancelFile(PQFileItem *i)
|
|
{
|
|
/* flag as cancelled */
|
|
/* iterate through fileItems and check for this one */
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::cancelFile(): " << std::endl;
|
|
i -> print(out);
|
|
out << std::endl;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
}
|
|
|
|
std::list<PQFileStatus *>::iterator it;
|
|
for(it = recvFiles.begin(); it != recvFiles.end(); it++)
|
|
{
|
|
if ((i->name==(*it)->fileItem->name) &&
|
|
(i->hash==(*it)->fileItem->hash))
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::cancelFile() ";
|
|
out << "Found file: " << i -> name;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
|
|
/* same */
|
|
(*it)->status = (PQIFILE_FAIL | PQIFILE_FAIL_CANCEL);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::cancelFile() ";
|
|
out << "couldn't match PQFileStatus!";
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int pqifiler::clearFailedTransfers()
|
|
{
|
|
/* remove all the failed items */
|
|
/* iterate through fileItems and check for this one */
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::clearFailedTransfers()");
|
|
|
|
std::list<PQFileStatus *>::iterator it;
|
|
int cleared = 0;
|
|
for(it = recvFiles.begin(); it != recvFiles.end(); /* done in loop */)
|
|
{
|
|
if ((*it)->status & PQIFILE_FAIL)
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::clearFailedTransfers() ";
|
|
out << "removing item: " << (*it) -> fileItem -> name;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
|
|
/* same */
|
|
PQFileStatus *cfile = (*it);
|
|
it = recvFiles.erase(it);
|
|
delete cfile;
|
|
cleared++;
|
|
}
|
|
else if ((*it)->status & PQIFILE_COMPLETE)
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::clearFailedTransfers() ";
|
|
out << "removing Completed item: ";
|
|
out << (*it) -> fileItem -> name;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
|
|
/* same */
|
|
PQFileStatus *cfile = (*it);
|
|
it = recvFiles.erase(it);
|
|
delete cfile;
|
|
cleared++;
|
|
}
|
|
else
|
|
{
|
|
it++;
|
|
}
|
|
}
|
|
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::clearFailedTransfers() cleared: ";
|
|
out << cleared;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
std::list<FileTransferItem *> pqifiler::getStatus()
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::getTransferStatus()");
|
|
std::list<FileTransferItem *> stateList;
|
|
|
|
/* iterate through all files to recv */
|
|
std::list<PQFileStatus *>::iterator it;
|
|
for(it = recvFiles.begin(); it != recvFiles.end(); it++)
|
|
{
|
|
FileTransferItem *fti = new FileTransferItem();
|
|
fti -> PQFileItem::copy((*it)->fileItem);
|
|
|
|
/* Fill in rate and State */
|
|
fti -> transferred = (*it)->recv_size;
|
|
fti -> crate = (*it)->rate / 1000.0; // kbytes.
|
|
fti -> trate = (*it)->rate / 1000.0; // kbytes.
|
|
fti -> lrate = (*it)->rate / 1000.0; // kbytes.
|
|
fti -> ltransfer = (*it)->req_size;
|
|
fti -> in = true;
|
|
|
|
/* get inactive period */
|
|
if ((*it) -> status == PQIFILE_NOT_ONLINE)
|
|
{
|
|
fti -> crate = 0;
|
|
fti -> trate = 0;
|
|
fti -> lrate = 0;
|
|
fti -> ltransfer = 0;
|
|
fti -> state = FT_STATE_OKAY;
|
|
}
|
|
else if ((*it) -> status & PQIFILE_FAIL)
|
|
{
|
|
fti -> crate = 0;
|
|
fti -> trate = 0;
|
|
fti -> lrate = 0;
|
|
fti -> ltransfer = 0;
|
|
fti -> state = FT_STATE_FAILED;
|
|
|
|
}
|
|
else if ((*it) -> status == PQIFILE_COMPLETE)
|
|
{
|
|
fti -> state = FT_STATE_COMPLETE;
|
|
}
|
|
else if ((*it) -> status == PQIFILE_DOWNLOADING)
|
|
{
|
|
fti -> state = FT_STATE_OKAY;
|
|
}
|
|
else
|
|
{
|
|
fti -> state = FT_STATE_FAILED;
|
|
}
|
|
stateList.push_back(fti);
|
|
}
|
|
return stateList;
|
|
}
|
|
|
|
|
|
/************* Incoming FileItems ******************************
|
|
*
|
|
* PQFileItem *pqifiler::sendPQFileItem()
|
|
* int pqifiler::recvPQFileItem(PQFileItem *in)
|
|
*
|
|
* * Worker Fns.
|
|
* int pqifiler::handleFileNotOnline(PQFileItem *in)
|
|
* int pqifiler::handleFileNotOnline(PQFileItem *in)
|
|
* int pqifiler::handleFileNotAvailable(PQFileItem *in)
|
|
* int pqifiler::handleFileData(PQFileItem *in)
|
|
* int pqifiler::handleFileRequest(PQFileItem *in)
|
|
* int pqifiler::handleFileCacheRequest(PQFileItem *req)
|
|
*
|
|
*/
|
|
|
|
PQItem *pqifiler::sendPQFileItem()
|
|
{
|
|
if (out_queue.size() < 1)
|
|
{
|
|
return NULL;
|
|
}
|
|
PQItem *i = out_queue.front();
|
|
out_queue.pop_front();
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::sendPQFileItem() ";
|
|
out << "returning: " << std::endl;
|
|
i -> print(out);
|
|
pqioutput(PQL_DEBUG_ALL, pqifilerzone, out.str());
|
|
}
|
|
return i;
|
|
}
|
|
|
|
int pqifiler::recvPQFileItem(PQItem *item)
|
|
{
|
|
/* check what type */
|
|
PQFileItem *in;
|
|
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::recvPQFileItem() ";
|
|
out << "input: " << std::endl;
|
|
item -> print(out);
|
|
pqioutput(PQL_DEBUG_ALL, pqifilerzone, out.str());
|
|
}
|
|
|
|
if (NULL == (in = dynamic_cast<PQFileItem *>(item)))
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::recvPQFileItem() Error Not PQFileItem");
|
|
delete item;
|
|
return 0;
|
|
}
|
|
|
|
|
|
switch(in -> subtype)
|
|
{
|
|
case PQI_FI_SUBTYPE_ERROR: /* not currently connected */
|
|
handleFileError(in);
|
|
break;
|
|
case PQI_FI_SUBTYPE_DATA: /* received some data */
|
|
handleFileData(in);
|
|
break;
|
|
case PQI_FI_SUBTYPE_REQUEST:
|
|
handleFileRequest(in);
|
|
break;
|
|
default:
|
|
/* what ? */
|
|
break;
|
|
}
|
|
|
|
/* clean up */
|
|
delete in;
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
int pqifiler::handleFileError(PQFileItem *in)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::handleFileError()");
|
|
/* get out the error */
|
|
if (in->fileoffset | PQIFILE_NOT_ONLINE)
|
|
{
|
|
return handleFileNotOnline(in);
|
|
}
|
|
if (in->fileoffset & PQIFILE_FAIL)
|
|
{
|
|
return handleFileNotAvailable(in);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int pqifiler::handleFileNotOnline(PQFileItem *in)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::handleFileNotOnline()");
|
|
/* flag recvFile item as not Online */
|
|
PQFileStatus *s = findRecvFileItem(in);
|
|
if ((!s) || (s -> status & PQIFILE_FAIL))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
s -> status = PQIFILE_NOT_ONLINE;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int pqifiler::handleFileNotAvailable(PQFileItem *in)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::handleFileNotAvailable()");
|
|
/* error - flag recvFile item with FAILED */
|
|
PQFileStatus *s = findRecvFileItem(in);
|
|
if (!s)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_AVAIL);
|
|
return 1;
|
|
}
|
|
|
|
|
|
int pqifiler::handleFileData(PQFileItem *in)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::handleFileData()");
|
|
/* find the right PQFileStatus */
|
|
PQFileStatus *recv = findRecvFileItem(in);
|
|
if (!recv)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::handleFileData() no matching PQFileStatus (current download)");
|
|
return 0;
|
|
}
|
|
|
|
if(recv->status & PQIFILE_FAIL)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::handleFileData() marked as FAIL");
|
|
return 0;
|
|
}
|
|
|
|
PQFileData *dta;
|
|
if (NULL == (dta = dynamic_cast<PQFileData *>(in)))
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::handleFileData() not PQFileData");
|
|
return 0;
|
|
}
|
|
|
|
/* first check the cid matches, so we can get it right for later */
|
|
if (0 != pqicid_cmp(&(in->cid), &(recv->fileItem->cid)))
|
|
{
|
|
/* not matched */
|
|
pqioutput(PQL_WARNING, pqifilerzone,
|
|
"pqifiler::handleFileData() correcting fileItem->cid");
|
|
pqicid_copy(&(in->cid), &(recv->fileItem->cid));
|
|
|
|
std::ostringstream out;
|
|
out << "pqifiler::handleFileData() in->cid != recv->fileItem->cid";
|
|
out << std::endl;
|
|
out << "in -> CID [" << in->cid.route[0];
|
|
for(int i = 0; i < 10; i++)
|
|
{
|
|
out << ":" << in->cid.route[i];
|
|
}
|
|
out << "]" << std::endl;
|
|
|
|
out << "recv -> CID [" << recv->fileItem->cid.route[0];
|
|
for(int i = 0; i < 10; i++)
|
|
{
|
|
out << ":" << recv->fileItem->cid.route[i];
|
|
}
|
|
out << "]" << std::endl;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,out.str());
|
|
}
|
|
|
|
|
|
/* add to file */
|
|
addFileData(recv, dta->fileoffset, dta->data, dta->datalen);
|
|
|
|
if (recv->status == PQIFILE_NOT_ONLINE)
|
|
{
|
|
/* switch to active */
|
|
recv->status = PQIFILE_DOWNLOADING;
|
|
}
|
|
|
|
/* if we have recieved all data - request some more */
|
|
if ((recv->recv_size == recv->req_loc + recv->req_size) &&
|
|
(recv->status != PQIFILE_COMPLETE))
|
|
{
|
|
requestData(recv);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int pqifiler::handleFileRequest(PQFileItem *in)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::handleFileRequest()");
|
|
/* see if in cache */
|
|
/* if yes send out chunk */
|
|
if (handleFileCacheRequest(in))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/* if not in cache - find file */
|
|
PQFileStatus *new_file = createFileCache(in);
|
|
if (!new_file)
|
|
{
|
|
/* bad file */
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::handleFileRequest() Failed to Load File-sendNotAvail");
|
|
return sendFileNotAvail(in);
|
|
}
|
|
|
|
fileCache.push_back(new_file);
|
|
|
|
return handleFileCacheRequest(in);
|
|
}
|
|
|
|
int pqifiler::handleFileCacheRequest(PQFileItem *req)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::handleFileCacheRequest()");
|
|
/* check if in cache */
|
|
bool found = false;
|
|
PQFileStatus *s;
|
|
std::list<PQFileStatus *>::iterator it;
|
|
for(it = fileCache.begin(); (!found) && (it != fileCache.end()); it++)
|
|
{
|
|
if ((req->name==(*it)->fileItem->name) &&
|
|
(req->hash==(*it)->fileItem->hash))
|
|
{
|
|
found = true;
|
|
s = (*it);
|
|
}
|
|
}
|
|
if (!found)
|
|
return 0;
|
|
|
|
/* push to out queue */
|
|
return generateFileData(s, req);
|
|
}
|
|
|
|
/************* Outgoing FileItems ******************************
|
|
*
|
|
* PQFileItem *pqifiler::sendPQFileItem()
|
|
*
|
|
* * Worker Fns.
|
|
* int pqifiler::tick();
|
|
* void pqifiler::queryInactive()
|
|
*
|
|
*/
|
|
|
|
|
|
int pqifiler::tick()
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::tick()");
|
|
/* check the status of recved files */
|
|
queryInactive();
|
|
|
|
/* this doesn't matter much how often it's ticked...
|
|
* if it generates Items, they will be detected other places.
|
|
* so we can return 0 (waiting)
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
|
|
void pqifiler::queryInactive()
|
|
{
|
|
std::ostringstream out;
|
|
|
|
out << "pqifiler::queryInactive()";
|
|
out << std::endl;
|
|
|
|
|
|
|
|
/* iterate through all files to recv */
|
|
int ts = time(NULL);
|
|
std::list<PQFileStatus *>::iterator it;
|
|
for(it = recvFiles.begin(); it != recvFiles.end(); it++)
|
|
{
|
|
/* get inactive period */
|
|
switch((*it) -> status)
|
|
{
|
|
case PQIFILE_NOT_ONLINE:
|
|
out << "File: " << (*it)->fileItem->name << " Not Online: ";
|
|
out << "Delta: " << (ts - (*it)->lastTS) << std::endl;
|
|
out << " LDelta: " << (*it)->lastDelta;
|
|
out << " Recved: " << (*it)->recv_size;
|
|
out << " Total: " << (*it)->total_size;
|
|
out << " LChunk: " << (*it)->req_size;
|
|
out << std::endl;
|
|
|
|
if (ts - ((*it)->lastTS) > PQIFILE_OFFLINE_CHECK)
|
|
{
|
|
resetFileTransfer(*it);
|
|
requestData(*it);
|
|
}
|
|
break;
|
|
case PQIFILE_DOWNLOADING:
|
|
out << "File: " << (*it)->fileItem->name << " Downloading: ";
|
|
out << " Delta: " << (ts - (*it)->lastTS) << std::endl;
|
|
out << " LDelta: " << (*it)->lastDelta;
|
|
out << " Recved: " << (*it)->recv_size;
|
|
out << " Total: " << (*it)->total_size;
|
|
out << " LChunk: " << (*it)->req_size;
|
|
out << std::endl;
|
|
|
|
if (ts - ((*it)->lastTS) > PQIFILE_DOWNLOAD_CHECK)
|
|
{
|
|
requestData(*it); /* give it a push */
|
|
}
|
|
break;
|
|
default:
|
|
out << "File: " << (*it)->fileItem->name << " Other mode: " << (*it)->status;
|
|
out << " Delta: " << (ts - (*it)->lastTS) << std::endl;
|
|
out << " LDelta: " << (*it)->lastDelta;
|
|
out << " Recved: " << (*it)->recv_size;
|
|
out << " Total: " << (*it)->total_size;
|
|
out << " LChunk: " << (*it)->req_size;
|
|
out << std::endl;
|
|
/* nothing */
|
|
break;
|
|
}
|
|
}
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
}
|
|
|
|
|
|
int pqifiler::requestData(PQFileStatus *item)
|
|
{
|
|
|
|
/* formulate a request for the next desired data chunk */
|
|
/* this handles the key throttling. This algorithm can
|
|
* be quite crude, as the tcp / and pqistreamer throttle as
|
|
* well.
|
|
*/
|
|
|
|
std::ostringstream out;
|
|
out << "pqifiler::requestData()" << std::endl;
|
|
|
|
/* get the time since last request */
|
|
int tm = time(NULL);
|
|
float delta = tm - item -> lastTS;
|
|
|
|
|
|
|
|
|
|
/* decide on transfer mode */
|
|
float max_rate;
|
|
switch(item->mode)
|
|
{
|
|
case TRANSFER_MODE_TRICKLE:
|
|
max_rate = TRANSFER_MODE_TRICKLE_RATE;
|
|
break;
|
|
case TRANSFER_MODE_NORMAL:
|
|
max_rate = TRANSFER_MODE_NORMAL_RATE;
|
|
break;
|
|
case TRANSFER_MODE_FAST:
|
|
max_rate = TRANSFER_MODE_FAST_RATE;
|
|
break;
|
|
default:
|
|
max_rate = TRANSFER_MODE_NORMAL_RATE;
|
|
break;
|
|
}
|
|
out << "max rate: " << max_rate;
|
|
out << std::endl;
|
|
|
|
/* not finished */
|
|
if (item->recv_size < item->req_loc + item->req_size)
|
|
{
|
|
if (delta > PQIFILE_DOWNLOAD_TIMEOUT)
|
|
{
|
|
/* we have timed out ... switch to
|
|
* offline
|
|
*/
|
|
/* start again slowly */
|
|
item->req_size = (int) (0.1 * max_rate);
|
|
out << "Timeout: switching to Offline.";
|
|
out << std::endl;
|
|
item->status = PQIFILE_NOT_ONLINE;
|
|
}
|
|
else
|
|
{
|
|
out << "Pause: Not Finished";
|
|
out << std::endl;
|
|
/* pause */
|
|
}
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
return 0;
|
|
}
|
|
|
|
|
|
if (delta <= PQIFILE_DOWNLOAD_MIN_DELTA)
|
|
{
|
|
/* pause */
|
|
out << "Small Delta -> Pause";
|
|
out << std::endl;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
return 0;
|
|
}
|
|
|
|
/* From this point - we will continue ... so handle rate now! */
|
|
/* calc rate */
|
|
float bytes_psec = item -> req_size / delta;
|
|
item -> rate = 0.9 * item -> rate + 0.1 * bytes_psec;
|
|
out << "delta: " << delta << " bytes: " << bytes_psec << " rate: " << item -> rate;
|
|
out << std::endl;
|
|
|
|
if (item->lastDelta <= PQIFILE_DOWNLOAD_TOO_FAST)
|
|
{
|
|
/* increase 0.75 of the calculated extra that could be transmitted
|
|
* in the timeframe
|
|
*/
|
|
|
|
float data_tf = item -> req_size;
|
|
float ldelta_f = item->lastDelta + 0.5; // 0.5 for extra space (+ dont / 0.0)
|
|
float tf_p_sec = data_tf / ldelta_f;
|
|
float extra_tf = tf_p_sec * (PQIFILE_DOWNLOAD_CHECK - ldelta_f);
|
|
|
|
item -> req_size = item->req_size + (int) (0.75 * extra_tf);
|
|
|
|
if (item->req_size > max_rate * PQIFILE_DOWNLOAD_CHECK)
|
|
item->req_size = (int) (max_rate * PQIFILE_DOWNLOAD_CHECK);
|
|
|
|
out << "Small Delta: " << ldelta_f << " (sec), rate: " << tf_p_sec;
|
|
out << std::endl;
|
|
out << "Small Delta Incrementing req_size from: " << data_tf;
|
|
out << " to :" << item->req_size;
|
|
out << std::endl;
|
|
|
|
}
|
|
else if (item->lastDelta > PQIFILE_DOWNLOAD_TOO_SLOW)
|
|
{
|
|
/* similarly decrease rate by 1.5 of extra time */
|
|
|
|
float data_tf = item -> req_size;
|
|
float ldelta_f = item->lastDelta + 0.5; // 0.5 for extra space (+ dont / 0.0)
|
|
float tf_p_sec = data_tf / ldelta_f;
|
|
float extra_tf = tf_p_sec * (ldelta_f - PQIFILE_DOWNLOAD_CHECK);
|
|
|
|
item -> req_size -= (int) (1.25 * extra_tf);
|
|
|
|
out << "Long Delta: " << ldelta_f << " (sec), rate: " << tf_p_sec;
|
|
out << std::endl;
|
|
out << "Long Delta Decrementing req_size from: " << data_tf;
|
|
out << " to :" << item->req_size;
|
|
out << std::endl;
|
|
}
|
|
|
|
/* make the packet */
|
|
|
|
item->req_loc = item->recv_size;
|
|
/* req_size already calculated (unless NULL) */
|
|
if (item->req_size < TRANSFER_START_MIN)
|
|
{
|
|
/* start again slowly
|
|
* added an extra limiter. */
|
|
item->req_size = (int) (0.01 * max_rate);
|
|
if (item->req_size < TRANSFER_START_MIN)
|
|
{
|
|
item->req_size = TRANSFER_START_MIN;
|
|
}
|
|
else if (item->req_size > TRANSFER_START_MAX)
|
|
{
|
|
item->req_size = TRANSFER_START_MAX;
|
|
}
|
|
}
|
|
|
|
out << "Making Packet: offset: " << item->req_loc << " size: " << item->req_size;
|
|
out << std::endl;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
|
|
PQFileItem *req = generatePQFileRequest(item);
|
|
out_queue.push_back(req);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/************* PQIFILEITEM Generator ***************************
|
|
*
|
|
* PQFileItem *pqifiler::generatePQFileRequest(PQFileStatus *s);
|
|
* int pqifiler::generateFileData(PQFileStatus *s, PQFileItem *req);
|
|
* int pqifiler::sendFileNotAvail(PQFileItem *req)
|
|
*
|
|
*/
|
|
|
|
|
|
int pqifiler::generateFileData(PQFileStatus *s, PQFileItem *req)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::generateFileData()");
|
|
|
|
if ((!s) || (!s->fd) || (s->status & PQIFILE_FAIL))
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::generateFileData() Bad Status");
|
|
if (!s)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::generateFileData() Bad Status (!s)");
|
|
}
|
|
if (!s->fd)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::generateFileData() Bad Status (!s->fd)");
|
|
}
|
|
if (s->status & PQIFILE_FAIL)
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::generateFileData() Bad Status (s->status): " << s->status;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,out.str());
|
|
}
|
|
|
|
/* return an error */
|
|
return sendFileNotAvail(req);
|
|
}
|
|
|
|
/* make the packets */
|
|
int tosend = req -> chunksize;
|
|
long base_loc = req -> fileoffset;
|
|
|
|
if (base_loc + tosend > s -> total_size)
|
|
{
|
|
tosend = s -> total_size - base_loc;
|
|
}
|
|
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::generateFileData() Sending " << tosend;
|
|
out << " bytes from offset: " << base_loc << std::endl;
|
|
out << "\tFrom File:" << req -> name;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
}
|
|
|
|
|
|
while(tosend > 0)
|
|
{
|
|
int pktsize = 5 * 1024;
|
|
if (pktsize > tosend)
|
|
pktsize = tosend;
|
|
|
|
/* seek for base_loc */
|
|
fseek(s->fd, base_loc, SEEK_SET);
|
|
|
|
// make a FileData type.
|
|
PQFileData *fd = new PQFileData();
|
|
|
|
// Copy details from the Request.
|
|
fd -> PQFileItem::copy(req);
|
|
|
|
// PQItem
|
|
fd -> sid = req -> sid;
|
|
fd -> type = PQI_ITEM_TYPE_FILEITEM;
|
|
fd -> subtype = PQI_FI_SUBTYPE_DATA;
|
|
|
|
// PQFileITem
|
|
fd -> size = s->fileItem->size; // total size of file.
|
|
fd -> fileoffset = base_loc;
|
|
fd -> chunksize = pktsize;
|
|
|
|
// data
|
|
fd -> datalen = pktsize;
|
|
fd -> data = malloc(fd -> datalen);
|
|
fd -> fileflags = 0;
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::generateFileData() pkt:" << std::endl;
|
|
//fd -> print(out);
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
}
|
|
|
|
/* read the data */
|
|
if (1 != fread(fd -> data, fd -> datalen, 1, s->fd))
|
|
{
|
|
std::ostringstream out;
|
|
|
|
out << "pqifiler::generateFileData() Read only: ";
|
|
out << fd->datalen << "/" << pktsize << " bytes of data - Discarding";
|
|
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
|
|
//free(fd->data);
|
|
//fd->data = NULL;
|
|
delete fd;
|
|
return 0;
|
|
}
|
|
|
|
/* decrement sizes */
|
|
base_loc += pktsize;
|
|
tosend -= pktsize;
|
|
|
|
/* send off the packet */
|
|
out_queue.push_back(fd);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
PQFileItem *pqifiler::generatePQFileRequest(PQFileStatus *s)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::generatePQFileRequest()");
|
|
|
|
PQFileItem *fi = s->fileItem->clone();
|
|
|
|
/* set req_loc, and req_size */
|
|
|
|
// PQItem
|
|
fi -> sid = getPQIsearchId();
|
|
fi -> type = PQI_ITEM_TYPE_FILEITEM;
|
|
fi -> subtype = PQI_FI_SUBTYPE_REQUEST;
|
|
|
|
// PQFileITem
|
|
fi -> size = s->fileItem->size; // total size of file.
|
|
fi -> fileoffset = s->req_loc;
|
|
fi -> chunksize = s->req_size;
|
|
|
|
std::ostringstream out;
|
|
|
|
out << "pqifiler::generatePQFileRequest() for: " << s->fileItem->name << std::endl;
|
|
out << "pqifiler::generatePQFileRequest() offset: " << fi->fileoffset << " chunksize: ";
|
|
out << fi->chunksize << std::endl;
|
|
|
|
//out << "s->fileItem:" << std::endl;
|
|
//s->fileItem->print(out);
|
|
//out << "DataRequest:" << std::endl;
|
|
//fi->print(out);
|
|
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
|
|
// timestamp request.
|
|
s->lastTS = time(NULL);
|
|
|
|
return fi;
|
|
}
|
|
|
|
int pqifiler::sendFileNotAvail(PQFileItem *req)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::sendFileNotAvail()");
|
|
PQFileItem *fi = req -> clone();
|
|
|
|
/* set error code */
|
|
fi -> subtype = PQI_FI_SUBTYPE_ERROR;
|
|
fi -> fileoffset = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_AVAIL);
|
|
|
|
/* send out */
|
|
out_queue.push_back(fi);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/************* FILE DATA HANDLING ******************************
|
|
*
|
|
* std::string pqifiler::determineFilePath(PQFileItem *item);
|
|
* int pqifiler::initiateFileTransfer(PQFileStatus *s);
|
|
* int pqifiler::resetFileTransfer(PQFileStatus *s);
|
|
* int pqifiler::addFileData(PQFileStatus *s, long idx, void *data, int size);
|
|
*
|
|
*/
|
|
|
|
std::string pqifiler::determineFilePath(PQFileItem *item)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::determineFilePath()");
|
|
|
|
/* get the download path */
|
|
// savePath = ".";
|
|
std::string filePath = savePath;
|
|
filePath += "/";
|
|
filePath += item->name;
|
|
return filePath;
|
|
}
|
|
|
|
int pqifiler::initiateFileTransfer(PQFileStatus *s)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::initiateFileTransfer()");
|
|
|
|
/* check if the file exists */
|
|
if (s->file_name.length() < 1)
|
|
{
|
|
s->file_name = determineFilePath(s->fileItem);
|
|
}
|
|
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::initiateFileTransfer() Filename: ";
|
|
out << s->file_name;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
}
|
|
|
|
/* attempt to open file */
|
|
FILE *fd = fopen(s->file_name.c_str(), "r+b");
|
|
if (!fd)
|
|
{
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::initiateFileTransfer() Failed to open: ";
|
|
out << s->file_name << " Error:" << errno;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
}
|
|
|
|
/* open in writing mode */
|
|
fd = fopen(s->file_name.c_str(), "w+b");
|
|
if (!fd)
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqifiler::initiateFileTransfer() Failed to open: ";
|
|
out << s->file_name << " Error:" << errno;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
|
|
/* failed to open the file */
|
|
s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_OPEN);
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/* if it opened, find it's length */
|
|
/* move to the end */
|
|
if (0 != fseek(fd, 0L, SEEK_END))
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::initiateFileTransfer() Seek Failed");
|
|
s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_SEEK);
|
|
return 0;
|
|
}
|
|
|
|
s->recv_size = ftell(fd); /* get the file length */
|
|
s->total_size = s->fileItem->size; /* save the total length */
|
|
s->fd = fd;
|
|
|
|
resetFileTransfer(s);
|
|
return 1;
|
|
}
|
|
|
|
|
|
int pqifiler::resetFileTransfer(PQFileStatus *state)
|
|
{
|
|
// reset all the basic items ... so the transfer will continue.
|
|
state->req_loc = 0;
|
|
state->req_size = 0;
|
|
state->lastTS = 0;
|
|
state->lastDelta = 0;
|
|
state->status = PQIFILE_NOT_ONLINE;
|
|
state->mode = TRANSFER_MODE_NORMAL;
|
|
state->rate = 0;
|
|
if (state->recv_size == state->total_size)
|
|
{
|
|
state->status = PQIFILE_COMPLETE;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
int pqifiler::addFileData(PQFileStatus *s, long idx, void *data, int size)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::addFileData()");
|
|
|
|
/* check the status */
|
|
if ((!s) || (!s->fd) || (s->status & PQIFILE_FAIL))
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::addFileData() Bad Status");
|
|
return 0;
|
|
}
|
|
|
|
/* check its at the correct location */
|
|
if ((idx != s->recv_size) || (s->recv_size + size > s->total_size))
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::addFileData() Bad Data Location");
|
|
return 0;
|
|
}
|
|
|
|
/* go to the end of the file */
|
|
if (0 != fseek(s->fd, 0L, SEEK_END))
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::addFileData() Bad fseek");
|
|
s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_SEEK);
|
|
return 0;
|
|
}
|
|
|
|
/* add the data */
|
|
if (1 != fwrite(data, size, 1, s->fd))
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::addFileData() Bad fwrite");
|
|
s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_WRITE);
|
|
return 0;
|
|
}
|
|
|
|
s->recv_size += size;
|
|
|
|
|
|
/* if we've completed the request this time */
|
|
if (s->req_loc + s->req_size == s->recv_size)
|
|
{
|
|
s->lastDelta = time(NULL) - s->lastTS;
|
|
}
|
|
|
|
if (s->recv_size == s->total_size)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::addFileData() File Complete!");
|
|
s->status = PQIFILE_COMPLETE;
|
|
fclose(s->fd);
|
|
// re-open in read mode (for transfers?)
|
|
s->fd = fopen(s->file_name.c_str(), "r+b");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
PQFileStatus *pqifiler::createFileCache(PQFileItem *in)
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::createFileCache()");
|
|
|
|
PQFileStatus *s = new PQFileStatus(in->clone());
|
|
|
|
/* request from fileindex */
|
|
bool found = false;
|
|
|
|
/* so here we will work with
|
|
*/
|
|
|
|
#ifdef USE_FILELOOK
|
|
PQFileItem *real = fileIndex -> findFileEntry(in);
|
|
if (real)
|
|
{
|
|
s->file_name = real -> path + "/" + real -> name;
|
|
found = true;
|
|
}
|
|
|
|
#else /*************************************************************************/
|
|
std::list<fdex *> flist = fileIndex -> findfilename(in->name);
|
|
std::list<fdex *>::iterator it;
|
|
for(it = flist.begin(); (!found) && (it != flist.end()); it++)
|
|
{
|
|
if (in -> size == (*it) -> len)
|
|
{
|
|
found = true;
|
|
s->file_name = (*it) -> path;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((!found) || (s->file_name.length() < 1))
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::createFileCache() Failed to Find File");
|
|
/* failed to open the file */
|
|
s->status = (PQIFILE_FAIL | PQIFILE_FAIL_BAD_PATH);
|
|
delete s;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* attempt to open file (readonly) */
|
|
FILE *fd = fopen(s->file_name.c_str(), "rb");
|
|
if (!fd)
|
|
{
|
|
std::stringstream out;
|
|
out << "pqifiler::createFileCache() Failed to Open the File" << std::endl;
|
|
out << "\tFull Path:" << s->file_name.c_str() << std::endl;
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone, out.str());
|
|
/* failed to open the file */
|
|
s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_OPEN);
|
|
delete s;
|
|
return NULL;
|
|
}
|
|
|
|
/* if it opened, find it's length */
|
|
/* move to the end */
|
|
if (0 != fseek(fd, 0L, SEEK_END))
|
|
{
|
|
pqioutput(PQL_DEBUG_BASIC, pqifilerzone,
|
|
"pqifiler::createFileCache() Fseek Failed");
|
|
|
|
s->status = (PQIFILE_FAIL | PQIFILE_FAIL_NOT_OPEN);
|
|
delete s;
|
|
return NULL;
|
|
}
|
|
|
|
s->recv_size = ftell(fd); /* get the file length */
|
|
s->total_size = s->fileItem->size; /* save the total length */
|
|
s->req_loc = 0; /* no request */
|
|
s->req_size = 0;
|
|
|
|
/* we are now ready for transfers */
|
|
s->fd = fd;
|
|
s->lastTS = 0;
|
|
s->status = PQIFILE_DOWNLOADING;
|
|
return s;
|
|
}
|