コード例 #1
0
ファイル: NZBFile.cpp プロジェクト: 0BruceWayne0/nzbget
void NZBFile::AddFileInfo(FileInfo* pFileInfo)
{
	// calculate file size and delete empty articles

	long long lSize = 0;
	long long lMissedSize = 0;
	long long lOneSize = 0;
	int iUncountedArticles = 0;
	int iMissedArticles = 0;
	FileInfo::Articles* pArticles = pFileInfo->GetArticles();
	int iTotalArticles = (int)pArticles->size();
	int i = 0;
	for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end(); )
	{
		ArticleInfo* pArticle = *it;
		if (!pArticle)
		{
			pArticles->erase(it);
			it = pArticles->begin() + i;
			iMissedArticles++;
			if (lOneSize > 0)
			{
				lMissedSize += lOneSize;
			}
			else
			{
				iUncountedArticles++;
			}
		}
		else
		{
			lSize += pArticle->GetSize();
			if (lOneSize == 0)
			{
				lOneSize = pArticle->GetSize();
			}
			it++;
			i++;
		}
	}

	if (pArticles->empty())
	{
		delete pFileInfo;
		return;
	}

	lMissedSize += iUncountedArticles * lOneSize;
	lSize += lMissedSize;
	m_pNZBInfo->GetFileList()->push_back(pFileInfo);
	pFileInfo->SetNZBInfo(m_pNZBInfo);
	pFileInfo->SetSize(lSize);
	pFileInfo->SetRemainingSize(lSize - lMissedSize);
	pFileInfo->SetMissedSize(lMissedSize);
	pFileInfo->SetTotalArticles(iTotalArticles);
	pFileInfo->SetMissedArticles(iMissedArticles);
}
コード例 #2
0
ファイル: QueueCoordinator.cpp プロジェクト: X0nic/nzbget
void QueueCoordinator::ResetHangingDownloads()
{
    if (g_pOptions->GetTerminateTimeout() == 0 && g_pOptions->GetArticleTimeout() == 0)
    {
        return;
    }

    DownloadQueue::Lock();
    time_t tm = ::time(NULL);

    for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
    {
        ArticleDownloader* pArticleDownloader = *it;

        if (tm - pArticleDownloader->GetLastUpdateTime() > g_pOptions->GetArticleTimeout() + 1 &&
                pArticleDownloader->GetStatus() == ArticleDownloader::adRunning)
        {
            error("Cancelling hanging download %s @ %s", pArticleDownloader->GetInfoName(),
                  pArticleDownloader->GetConnectionName());
            pArticleDownloader->Stop();
        }

        if (tm - pArticleDownloader->GetLastUpdateTime() > g_pOptions->GetTerminateTimeout() &&
                pArticleDownloader->GetStatus() == ArticleDownloader::adRunning)
        {
            ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
            debug("Terminating hanging download %s", pArticleDownloader->GetInfoName());
            if (pArticleDownloader->Terminate())
            {
                error("Terminated hanging download %s @ %s", pArticleDownloader->GetInfoName(),
                      pArticleDownloader->GetConnectionName());
                pArticleInfo->SetStatus(ArticleInfo::aiUndefined);
            }
            else
            {
                error("Could not terminate hanging download %s @ %s", pArticleDownloader->GetInfoName(),
                      pArticleDownloader->GetConnectionName());
            }
            m_ActiveDownloads.erase(it);

            pArticleDownloader->GetFileInfo()->SetActiveDownloads(pArticleDownloader->GetFileInfo()->GetActiveDownloads() - 1);
            pArticleDownloader->GetFileInfo()->GetNZBInfo()->SetActiveDownloads(pArticleDownloader->GetFileInfo()->GetNZBInfo()->GetActiveDownloads() - 1);
            pArticleDownloader->GetFileInfo()->GetNZBInfo()->SetDownloadedSize(pArticleDownloader->GetFileInfo()->GetNZBInfo()->GetDownloadedSize() + pArticleDownloader->GetDownloadedSize());

            // it's not safe to destroy pArticleDownloader, because the state of object is unknown
            delete pArticleDownloader;
            it = m_ActiveDownloads.begin();
            continue;
        }
        it++;
    }

    DownloadQueue::Unlock();
}
コード例 #3
0
ParChecker::EFileStatus ParCoordinator::PostParChecker::FindFileCrc(const char* szFilename,
	unsigned long* lCrc, SegmentList* pSegments)
{
	CompletedFile* pCompletedFile = NULL;

	for (CompletedFiles::iterator it = m_pPostInfo->GetNZBInfo()->GetCompletedFiles()->begin(); it != m_pPostInfo->GetNZBInfo()->GetCompletedFiles()->end(); it++)
	{
		CompletedFile* pCompletedFile2 = *it;
		if (!strcasecmp(pCompletedFile2->GetFileName(), szFilename))
		{
			pCompletedFile = pCompletedFile2;
			break;
		}
	}
	if (!pCompletedFile)
	{
		return ParChecker::fsUnknown;
	}

	debug("Found completed file: %s, CRC: %.8x, Status: %i", Util::BaseFileName(pCompletedFile->GetFileName()), pCompletedFile->GetCrc(), (int)pCompletedFile->GetStatus());

	*lCrc = pCompletedFile->GetCrc();

	if (pCompletedFile->GetStatus() == CompletedFile::cfPartial && pCompletedFile->GetID() > 0 &&
		!m_pPostInfo->GetNZBInfo()->GetReprocess())
	{
		FileInfo* pTmpFileInfo = new FileInfo(pCompletedFile->GetID());

		if (!g_pDiskState->LoadFileState(pTmpFileInfo, NULL, true))
		{
			delete pTmpFileInfo;
			return ParChecker::fsUnknown;
		}

		for (FileInfo::Articles::iterator it = pTmpFileInfo->GetArticles()->begin(); it != pTmpFileInfo->GetArticles()->end(); it++)
		{
			ArticleInfo* pa = *it;
			ParChecker::Segment* pSegment = new Segment(pa->GetStatus() == ArticleInfo::aiFinished,
				pa->GetSegmentOffset(), pa->GetSegmentSize(), pa->GetCrc());
			pSegments->push_back(pSegment);
		}

		delete pTmpFileInfo;
	}

	return pCompletedFile->GetStatus() == CompletedFile::cfSuccess ? ParChecker::fsSuccess :
		pCompletedFile->GetStatus() == CompletedFile::cfFailure &&
			!m_pPostInfo->GetNZBInfo()->GetReprocess() ? ParChecker::fsFailure :
		pCompletedFile->GetStatus() == CompletedFile::cfPartial && pSegments->size() > 0 &&
			!m_pPostInfo->GetNZBInfo()->GetReprocess()? ParChecker::fsPartial :
		ParChecker::fsUnknown;
}
コード例 #4
0
ファイル: NZBFile.cpp プロジェクト: 0BruceWayne0/nzbget
void NZBFile::CalcHashes()
{
	TempFileList fileList;

	for (FileList::iterator it = m_pNZBInfo->GetFileList()->begin(); it != m_pNZBInfo->GetFileList()->end(); it++)
	{
		fileList.push_back(*it);
	}

	fileList.sort(CompareFileInfo);

	unsigned int iFullContentHash = 0;
	unsigned int iFilteredContentHash = 0;
	int iUseForFilteredCount = 0;

	for (TempFileList::iterator it = fileList.begin(); it != fileList.end(); it++)
	{
		FileInfo* pFileInfo = *it;

		// check file extension
		bool bSkip = !pFileInfo->GetParFile() &&
			Util::MatchFileExt(pFileInfo->GetFilename(), g_pOptions->GetExtCleanupDisk(), ",;");

		for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
		{
			ArticleInfo* pArticle = *it;
			int iLen = strlen(pArticle->GetMessageID());
			iFullContentHash = Util::HashBJ96(pArticle->GetMessageID(), iLen, iFullContentHash);
			if (!bSkip)
			{
				iFilteredContentHash = Util::HashBJ96(pArticle->GetMessageID(), iLen, iFilteredContentHash);
				iUseForFilteredCount++;
			}
		}
	}

	// if filtered hash is based on less than a half of files - do not use filtered hash at all
	if (iUseForFilteredCount < (int)fileList.size() / 2)
	{
		iFilteredContentHash = 0;
	}

	m_pNZBInfo->SetFullContentHash(iFullContentHash);
	m_pNZBInfo->SetFilteredContentHash(iFilteredContentHash);
}
コード例 #5
0
ファイル: QueueCoordinator.cpp プロジェクト: X0nic/nzbget
void QueueCoordinator::DiscardDiskFile(FileInfo* pFileInfo)
{
    // deleting temporary files

    if (!g_pOptions->GetDirectWrite())
    {
        for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
        {
            ArticleInfo* pa = *it;
            if (pa->GetResultFilename())
            {
                remove(pa->GetResultFilename());
            }
        }
    }

    if (g_pOptions->GetDirectWrite() && pFileInfo->GetOutputFilename())
    {
        remove(pFileInfo->GetOutputFilename());
    }
}
コード例 #6
0
ファイル: ArticleDownloader.cpp プロジェクト: Bootz/nzbget
void ArticleDownloader::CompleteFileParts()
{
	debug("Completing file parts");
	debug("ArticleFilename: %s", m_pFileInfo->GetFilename());

	SetStatus(adJoining);

	bool bDirectWrite = g_pOptions->GetDirectWrite() && m_pFileInfo->GetOutputInitialized();

	char szNZBName[1024];
	char szNZBDestDir[1024];
	// the locking is needed for accessing the memebers of NZBInfo
	g_pDownloadQueueHolder->LockQueue();
	strncpy(szNZBName, m_pFileInfo->GetNZBInfo()->GetName(), 1024);
	strncpy(szNZBDestDir, m_pFileInfo->GetNZBInfo()->GetDestDir(), 1024);
	g_pDownloadQueueHolder->UnlockQueue();
	szNZBName[1024-1] = '\0';
	szNZBDestDir[1024-1] = '\0';
	
	char InfoFilename[1024];
	snprintf(InfoFilename, 1024, "%s%c%s", szNZBName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
	InfoFilename[1024-1] = '\0';

	if (!g_pOptions->GetDecode())
	{
		detail("Moving articles for %s", InfoFilename);
	}
	else if (bDirectWrite)
	{
		detail("Checking articles for %s", InfoFilename);
	}
	else
	{
		detail("Joining articles for %s", InfoFilename);
	}

	// Ensure the DstDir is created
	char szErrBuf[1024];
	if (!Util::ForceDirectories(szNZBDestDir, szErrBuf, sizeof(szErrBuf)))
	{
		error("Could not create directory %s: %s", szNZBDestDir, szErrBuf);
		SetStatus(adJoined);
		return;
	}

	char ofn[1024];
	snprintf(ofn, 1024, "%s%c%s", szNZBDestDir, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
	ofn[1024-1] = '\0';

	// prevent overwriting existing files
	int dupcount = 0;
	while (Util::FileExists(ofn))
	{
		dupcount++;
		snprintf(ofn, 1024, "%s%c%s_duplicate%d", szNZBDestDir, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename(), dupcount);
		ofn[1024-1] = '\0';
	}

	FILE* outfile = NULL;
	char tmpdestfile[1024];
	snprintf(tmpdestfile, 1024, "%s.tmp", ofn);
	tmpdestfile[1024-1] = '\0';

	if (g_pOptions->GetDecode() && !bDirectWrite)
	{
		remove(tmpdestfile);
		outfile = fopen(tmpdestfile, "wb+");
		if (!outfile)
		{
			error("Could not create file %s!", tmpdestfile);
			SetStatus(adJoined);
			return;
		}
		if (g_pOptions->GetWriteBufferSize() == -1 && (*m_pFileInfo->GetArticles())[0])
		{
			setvbuf(outfile, (char *)NULL, _IOFBF, (*m_pFileInfo->GetArticles())[0]->GetSize());
		}
		else if (g_pOptions->GetWriteBufferSize() > 0)
		{
			setvbuf(outfile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
		}
	}
	else if (!g_pOptions->GetDecode())
	{
		remove(tmpdestfile);
		if (!Util::CreateDirectory(ofn))
		{
			error("Could not create directory %s! Errcode: %i", ofn, errno);
			SetStatus(adJoined);
			return;
		}
	}

	bool complete = true;
	int iBrokenCount = 0;
	static const int BUFFER_SIZE = 1024 * 50;
	char* buffer = NULL;

	if (g_pOptions->GetDecode() && !bDirectWrite)
	{
		buffer = (char*)malloc(BUFFER_SIZE);
	}

	for (FileInfo::Articles::iterator it = m_pFileInfo->GetArticles()->begin(); it != m_pFileInfo->GetArticles()->end(); it++)
	{
		ArticleInfo* pa = *it;
		if (pa->GetStatus() != ArticleInfo::aiFinished)
		{
			iBrokenCount++;
			complete = false;
		}
		else if (g_pOptions->GetDecode() && !bDirectWrite)
		{
			FILE* infile;
			const char* fn = pa->GetResultFilename();

			infile = fopen(fn, "rb");
			if (infile)
			{
				int cnt = BUFFER_SIZE;

				while (cnt == BUFFER_SIZE)
				{
					cnt = (int)fread(buffer, 1, BUFFER_SIZE, infile);
					fwrite(buffer, 1, cnt, outfile);
					SetLastUpdateTimeNow();
				}

				fclose(infile);
			}
			else
			{
				complete = false;
				iBrokenCount++;
				detail("Could not find file %s. Status is broken", fn);
			}
		}
		else if (!g_pOptions->GetDecode())
		{
			const char* fn = pa->GetResultFilename();
			char dstFileName[1024];
			snprintf(dstFileName, 1024, "%s%c%03i", ofn, (int)PATH_SEPARATOR, pa->GetPartNumber());
			dstFileName[1024-1] = '\0';
			if (!Util::MoveFile(fn, dstFileName))
			{
				error("Could not move file %s to %s! Errcode: %i", fn, dstFileName, errno);
			}
		}
	}

	if (buffer)
	{
		free(buffer);
	}

	if (outfile)
	{
		fclose(outfile);
		if (!Util::MoveFile(tmpdestfile, ofn))
		{
			error("Could not move file %s to %s! Errcode: %i", tmpdestfile, ofn, errno);
		}
	}

	if (bDirectWrite)
	{
		if (!Util::MoveFile(m_szOutputFilename, ofn))
		{
			error("Could not move file %s to %s! Errcode: %i", m_szOutputFilename, ofn, errno);
		}

		// if destination directory was changed delete the old directory (if empty)
		int iLen = strlen(szNZBDestDir);
		if (!(!strncmp(szNZBDestDir, m_szOutputFilename, iLen) && 
			(m_szOutputFilename[iLen] == PATH_SEPARATOR || m_szOutputFilename[iLen] == ALT_PATH_SEPARATOR)))
		{
			debug("Checking old dir for: %s", m_szOutputFilename);
			char szOldDestDir[1024];
			int iMaxlen = Util::BaseFileName(m_szOutputFilename) - m_szOutputFilename;
			if (iMaxlen > 1024-1) iMaxlen = 1024-1;
			strncpy(szOldDestDir, m_szOutputFilename, iMaxlen);
			szOldDestDir[iMaxlen] = '\0';
			if (Util::DirEmpty(szOldDestDir))
			{
				debug("Deleting old dir: %s", szOldDestDir);
				rmdir(szOldDestDir);
			}
		}
	}

	if (!bDirectWrite || g_pOptions->GetContinuePartial())
	{
		for (FileInfo::Articles::iterator it = m_pFileInfo->GetArticles()->begin(); it != m_pFileInfo->GetArticles()->end(); it++)
		{
			ArticleInfo* pa = *it;
			remove(pa->GetResultFilename());
		}
	}

	if (complete)
	{
		info("Successfully downloaded %s", InfoFilename);
	}
	else
	{
		warn("%i of %i article downloads failed for \"%s\"", iBrokenCount, m_pFileInfo->GetArticles()->size(), InfoFilename);

		if (g_pOptions->GetCreateBrokenLog())
		{
			char szBrokenLogName[1024];
			snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", szNZBDestDir, (int)PATH_SEPARATOR);
			szBrokenLogName[1024-1] = '\0';
			FILE* file = fopen(szBrokenLogName, "ab");
			fprintf(file, "%s (%i/%i)%s", m_pFileInfo->GetFilename(), m_pFileInfo->GetArticles()->size() - iBrokenCount, m_pFileInfo->GetArticles()->size(), LINE_ENDING);
			fclose(file);
		}
	}

	// the locking is needed for accessing the memebers of NZBInfo
	g_pDownloadQueueHolder->LockQueue();
	m_pFileInfo->GetNZBInfo()->GetCompletedFiles()->push_back(strdup(ofn));
	if (strcmp(m_pFileInfo->GetNZBInfo()->GetDestDir(), szNZBDestDir))
	{
		// destination directory was changed during completion, need to move the file
		MoveCompletedFiles(m_pFileInfo->GetNZBInfo(), szNZBDestDir);
	}
	g_pDownloadQueueHolder->UnlockQueue();

	SetStatus(adJoined);
}
コード例 #7
0
ファイル: NZBFile.cpp プロジェクト: 0BruceWayne0/nzbget
bool NZBFile::ParseNZB(IUnknown* nzb)
{
	MSXML::IXMLDOMDocumentPtr doc = nzb;
	MSXML::IXMLDOMNodePtr root = doc->documentElement;

	MSXML::IXMLDOMNodePtr node = root->selectSingleNode("/nzb/head/meta[@type='password']");
	if (node)
	{
		_bstr_t password(node->Gettext());
		m_szPassword = strdup(password);
	}

	MSXML::IXMLDOMNodeListPtr fileList = root->selectNodes("/nzb/file");
	for (int i = 0; i < fileList->Getlength(); i++)
	{
		node = fileList->Getitem(i);
		MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("subject");
		if (!attribute) return false;
		_bstr_t subject(attribute->Gettext());
        FileInfo* pFileInfo = new FileInfo();
		pFileInfo->SetSubject(subject);

		attribute = node->Getattributes()->getNamedItem("date");
		if (attribute)
		{
			_bstr_t date(attribute->Gettext());
			pFileInfo->SetTime(atoi(date));
		}

		MSXML::IXMLDOMNodeListPtr groupList = node->selectNodes("groups/group");
		for (int g = 0; g < groupList->Getlength(); g++)
		{
			MSXML::IXMLDOMNodePtr node = groupList->Getitem(g);
			_bstr_t group = node->Gettext();
			pFileInfo->GetGroups()->push_back(strdup((const char*)group));
		}

		MSXML::IXMLDOMNodeListPtr segmentList = node->selectNodes("segments/segment");
		for (int g = 0; g < segmentList->Getlength(); g++)
		{
			MSXML::IXMLDOMNodePtr node = segmentList->Getitem(g);
			_bstr_t id = node->Gettext();
            char szId[2048];
            snprintf(szId, 2048, "<%s>", (const char*)id);

			MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("number");
			if (!attribute) return false;
			_bstr_t number(attribute->Gettext());

			attribute = node->Getattributes()->getNamedItem("bytes");
			if (!attribute) return false;
			_bstr_t bytes(attribute->Gettext());

			int partNumber = atoi(number);
			int lsize = atoi(bytes);

			if (partNumber > 0)
			{
				ArticleInfo* pArticle = new ArticleInfo();
				pArticle->SetPartNumber(partNumber);
				pArticle->SetMessageID(szId);
				pArticle->SetSize(lsize);
				AddArticle(pFileInfo, pArticle);
			}
		}

		AddFileInfo(pFileInfo);
	}
	return true;
}
コード例 #8
0
ファイル: QueueCoordinator.cpp プロジェクト: X0nic/nzbget
void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
{
    debug("Article downloaded");

    FileInfo* pFileInfo = pArticleDownloader->GetFileInfo();
    NZBInfo* pNZBInfo = pFileInfo->GetNZBInfo();
    ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
    bool bRetry = false;
    bool fileCompleted = false;

    DownloadQueue* pDownloadQueue = DownloadQueue::Lock();

    if (pArticleDownloader->GetStatus() == ArticleDownloader::adFinished)
    {
        pArticleInfo->SetStatus(ArticleInfo::aiFinished);
        pFileInfo->SetSuccessSize(pFileInfo->GetSuccessSize() + pArticleInfo->GetSize());
        pNZBInfo->SetCurrentSuccessSize(pNZBInfo->GetCurrentSuccessSize() + pArticleInfo->GetSize());
        pNZBInfo->SetParCurrentSuccessSize(pNZBInfo->GetParCurrentSuccessSize() + (pFileInfo->GetParFile() ? pArticleInfo->GetSize() : 0));
        pFileInfo->SetSuccessArticles(pFileInfo->GetSuccessArticles() + 1);
        pNZBInfo->SetCurrentSuccessArticles(pNZBInfo->GetCurrentSuccessArticles() + 1);
    }
    else if (pArticleDownloader->GetStatus() == ArticleDownloader::adFailed)
    {
        pArticleInfo->SetStatus(ArticleInfo::aiFailed);
        pFileInfo->SetFailedSize(pFileInfo->GetFailedSize() + pArticleInfo->GetSize());
        pNZBInfo->SetCurrentFailedSize(pNZBInfo->GetCurrentFailedSize() + pArticleInfo->GetSize());
        pNZBInfo->SetParCurrentFailedSize(pNZBInfo->GetParCurrentFailedSize() + (pFileInfo->GetParFile() ? pArticleInfo->GetSize() : 0));
        pFileInfo->SetFailedArticles(pFileInfo->GetFailedArticles() + 1);
        pNZBInfo->SetCurrentFailedArticles(pNZBInfo->GetCurrentFailedArticles() + 1);
    }
    else if (pArticleDownloader->GetStatus() == ArticleDownloader::adRetry)
    {
        pArticleInfo->SetStatus(ArticleInfo::aiUndefined);
        bRetry = true;
    }

    if (!bRetry)
    {
        pFileInfo->SetRemainingSize(pFileInfo->GetRemainingSize() - pArticleInfo->GetSize());
        pNZBInfo->SetRemainingSize(pNZBInfo->GetRemainingSize() - pArticleInfo->GetSize());
        if (pFileInfo->GetPaused())
        {
            pNZBInfo->SetPausedSize(pNZBInfo->GetPausedSize() - pArticleInfo->GetSize());
        }
        pFileInfo->SetCompletedArticles(pFileInfo->GetCompletedArticles() + 1);
        fileCompleted = (int)pFileInfo->GetArticles()->size() == pFileInfo->GetCompletedArticles();
        pFileInfo->GetServerStats()->ListOp(pArticleDownloader->GetServerStats(), ServerStatList::soAdd);
        pNZBInfo->GetCurrentServerStats()->ListOp(pArticleDownloader->GetServerStats(), ServerStatList::soAdd);
        pFileInfo->SetPartialChanged(true);
    }

    if (!pFileInfo->GetFilenameConfirmed() &&
            pArticleDownloader->GetStatus() == ArticleDownloader::adFinished &&
            pArticleDownloader->GetArticleFilename())
    {
        pFileInfo->SetFilename(pArticleDownloader->GetArticleFilename());
        pFileInfo->SetFilenameConfirmed(true);
        if (g_pOptions->GetDupeCheck() &&
                pNZBInfo->GetDupeMode() != dmForce &&
                !pNZBInfo->GetManyDupeFiles() &&
                Util::FileExists(pNZBInfo->GetDestDir(), pFileInfo->GetFilename()))
        {
            warn("File \"%s\" seems to be duplicate, cancelling download and deleting file from queue", pFileInfo->GetFilename());
            fileCompleted = false;
            pFileInfo->SetAutoDeleted(true);
            DeleteQueueEntry(pDownloadQueue, pFileInfo);
        }
    }

    pNZBInfo->SetDownloadedSize(pNZBInfo->GetDownloadedSize() + pArticleDownloader->GetDownloadedSize());

    bool deleteFileObj = false;

    if (fileCompleted && !pFileInfo->GetDeleted())
    {
        // all jobs done
        DownloadQueue::Unlock();
        pArticleDownloader->CompleteFileParts();
        pDownloadQueue = DownloadQueue::Lock();
        deleteFileObj = true;
    }

    CheckHealth(pDownloadQueue, pFileInfo);

    bool hasOtherDownloaders = false;
    for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
    {
        ArticleDownloader* pDownloader = *it;
        if (pDownloader != pArticleDownloader && pDownloader->GetFileInfo() == pFileInfo)
        {
            hasOtherDownloaders = true;
            break;
        }
    }
    deleteFileObj |= pFileInfo->GetDeleted() && !hasOtherDownloaders;

    // remove downloader from downloader list
    m_ActiveDownloads.erase(std::find(m_ActiveDownloads.begin(), m_ActiveDownloads.end(), pArticleDownloader));

    pFileInfo->SetActiveDownloads(pFileInfo->GetActiveDownloads() - 1);
    pNZBInfo->SetActiveDownloads(pNZBInfo->GetActiveDownloads() - 1);

    if (deleteFileObj)
    {
        DeleteFileInfo(pDownloadQueue, pFileInfo, fileCompleted);
        pDownloadQueue->Save();
    }

    DownloadQueue::Unlock();
}