2
* This file is part of nzbget
4
* Copyright (C) 2007-2009 Andrei Prygounkov <hugbug@users.sourceforge.net>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
* $Date: 2009-05-27 23:09:08 +0200 (Wed, 27 May 2009) $
34
#ifndef DISABLE_PARCHECK
41
#include <par2cmdline.h>
42
#include <par2repairer.h>
44
#include <libpar2/par2cmdline.h>
45
#include <libpar2/par2repairer.h>
49
#include "ParChecker.h"
54
extern Options* g_pOptions;
56
const char* Par2CmdLineErrStr[] = { "OK",
57
"data files are damaged and there is enough recovery data available to repair them",
58
"data files are damaged and there is insufficient recovery data available to be able to repair them",
59
"there was something wrong with the command line arguments",
60
"the PAR2 files did not contain sufficient information about the data files to be able to verify them",
61
"repair completed but the data files still appear to be damaged",
62
"an error occured when accessing files",
63
"internal error occurred",
67
class Repairer : public Par2Repairer
69
friend class ParChecker;
73
ParChecker::ParChecker()
75
debug("Creating ParChecker");
77
m_eStatus = psUndefined;
78
m_szParFilename = NULL;
81
m_szProgressLabel = (char*)malloc(1024);
85
m_bVerifyingExtraFiles = false;
87
m_eStage = ptLoadingPars;
88
m_QueuedParFiles.clear();
91
ParChecker::~ParChecker()
93
debug("Destroying ParChecker");
97
free(m_szParFilename);
107
free(m_szProgressLabel);
109
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
113
m_QueuedParFiles.clear();
116
void ParChecker::SetParFilename(const char * szParFilename)
120
free(m_szParFilename);
122
m_szParFilename = strdup(szParFilename);
125
void ParChecker::SetInfoName(const char * szInfoName)
131
m_szInfoName = strdup(szInfoName);
134
void ParChecker::SetStatus(EStatus eStatus)
140
void ParChecker::Run()
142
m_bRepairNotNeeded = false;
143
m_eStage = ptLoadingPars;
144
m_iProcessedFiles = 0;
146
m_bVerifyingExtraFiles = false;
147
m_bCancelled = false;
149
info("Verifying %s", m_szInfoName);
150
SetStatus(psWorking);
152
debug("par: %s", m_szParFilename);
153
CommandLine commandLine;
154
const char* argv[] = { "par2", "r", "-v", "-v", m_szParFilename };
155
if (!commandLine.Parse(5, (char**)argv))
157
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
164
Repairer* pRepairer = new Repairer();
165
m_pRepairer = pRepairer;
167
pRepairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
168
pRepairer->sig_progress.connect(sigc::mem_fun(*this, &ParChecker::signal_progress));
169
pRepairer->sig_done.connect(sigc::mem_fun(*this, &ParChecker::signal_done));
171
snprintf(m_szProgressLabel, 1024, "Verifying %s", m_szInfoName);
172
m_szProgressLabel[1024-1] = '\0';
174
m_iStageProgress = 0;
177
res = pRepairer->PreProcess(commandLine);
178
debug("ParChecker: PreProcess-result=%i", res);
180
if (res != eSuccess || IsStopped())
182
error("Could not verify %s: %s", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
183
m_szErrMsg = strdup("par2-file could not be processed");
189
char BufReason[1024];
197
m_eStage = ptVerifyingSources;
198
res = pRepairer->Process(commandLine, false);
199
debug("ParChecker: Process-result=%i", res);
201
if (!IsStopped() && res == eRepairNotPossible && CheckSplittedFragments())
203
pRepairer->UpdateVerificationResults();
204
res = pRepairer->Process(commandLine, false);
205
debug("ParChecker: Process-result=%i", res);
208
bool bMoreFilesLoaded = true;
209
while (!IsStopped() && res == eRepairNotPossible)
211
int missingblockcount = pRepairer->missingblockcount - pRepairer->recoverypacketmap.size();
212
if (bMoreFilesLoaded)
214
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
217
m_mutexQueuedParFiles.Lock();
218
bool hasMorePars = !m_QueuedParFiles.empty();
219
m_mutexQueuedParFiles.Unlock();
224
bool requested = RequestMorePars(missingblockcount, &iBlockFound);
227
strncpy(m_szProgressLabel, "Awaiting additional par-files", 1024);
228
m_szProgressLabel[1024-1] = '\0';
233
m_mutexQueuedParFiles.Lock();
234
hasMorePars = !m_QueuedParFiles.empty();
235
m_bQueuedParFilesChanged = false;
236
m_mutexQueuedParFiles.Unlock();
238
if (!requested && !hasMorePars)
240
snprintf(BufReason, 1024, "not enough par-blocks, %i block(s) needed, but %i block(s) available", missingblockcount, iBlockFound);
241
BufReason[1024-1] = '\0';
242
m_szErrMsg = strdup(BufReason);
248
// wait until new files are added by "AddParFile" or a change is signaled by "QueueChanged"
249
bool bQueuedParFilesChanged = false;
250
while (!bQueuedParFilesChanged && !IsStopped())
252
m_mutexQueuedParFiles.Lock();
253
bQueuedParFilesChanged = m_bQueuedParFilesChanged;
254
m_mutexQueuedParFiles.Unlock();
265
bMoreFilesLoaded = LoadMorePars();
266
if (bMoreFilesLoaded)
268
pRepairer->UpdateVerificationResults();
269
res = pRepairer->Process(commandLine, false);
270
debug("ParChecker: Process-result=%i", res);
283
info("Repair not needed for %s", m_szInfoName);
284
m_bRepairNotNeeded = true;
286
else if (res == eRepairPossible)
288
if (g_pOptions->GetParRepair())
290
info("Repairing %s", m_szInfoName);
292
snprintf(m_szProgressLabel, 1024, "Repairing %s", m_szInfoName);
293
m_szProgressLabel[1024-1] = '\0';
295
m_iStageProgress = 0;
296
m_iProcessedFiles = 0;
297
m_eStage = ptRepairing;
298
m_iFilesToRepair = pRepairer->damagedfilecount + pRepairer->missingfilecount;
301
res = pRepairer->Process(commandLine, true);
302
debug("ParChecker: Process-result=%i", res);
305
info("Successfully repaired %s", m_szInfoName);
310
info("Repair possible for %s", m_szInfoName);
317
warn("Repair cancelled for %s", m_szInfoName);
318
m_szErrMsg = strdup("repair cancelled");
321
else if (res == eSuccess)
323
SetStatus(psFinished);
327
if (!m_szErrMsg && (int)res >= 0 && (int)res <= 8)
329
m_szErrMsg = strdup(Par2CmdLineErrStr[res]);
331
error("Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
338
bool ParChecker::LoadMorePars()
340
m_mutexQueuedParFiles.Lock();
341
QueuedParFiles moreFiles;
342
moreFiles.assign(m_QueuedParFiles.begin(), m_QueuedParFiles.end());
343
m_QueuedParFiles.clear();
344
m_mutexQueuedParFiles.Unlock();
346
for (QueuedParFiles::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
348
char* szParFilename = *it;
349
bool loadedOK = ((Repairer*)m_pRepairer)->LoadPacketsFromFile(szParFilename);
352
info("File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
356
info("Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
361
return !moreFiles.empty();
364
void ParChecker::AddParFile(const char * szParFilename)
366
m_mutexQueuedParFiles.Lock();
367
m_QueuedParFiles.push_back(strdup(szParFilename));
368
m_bQueuedParFilesChanged = true;
369
m_mutexQueuedParFiles.Unlock();
372
void ParChecker::QueueChanged()
374
m_mutexQueuedParFiles.Lock();
375
m_bQueuedParFilesChanged = true;
376
m_mutexQueuedParFiles.Unlock();
379
bool ParChecker::CheckSplittedFragments()
381
bool bFragmentsAdded = false;
383
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
384
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
386
Par2RepairerSourceFile *sourcefile = *it;
387
if (!sourcefile->GetTargetExists() && AddSplittedFragments(sourcefile->TargetFileName().c_str()))
389
bFragmentsAdded = true;
393
return bFragmentsAdded;
396
bool ParChecker::AddSplittedFragments(const char* szFilename)
398
char szDirectory[1024];
399
strncpy(szDirectory, szFilename, 1024);
400
szDirectory[1024-1] = '\0';
402
char* szBasename = Util::BaseFileName(szDirectory);
403
if (szBasename == szDirectory)
407
szBasename[-1] = '\0';
408
int iBaseLen = strlen(szBasename);
410
list<CommandLine::ExtraFile> extrafiles;
412
DirBrowser dir(szDirectory);
413
while (const char* filename = dir.Next())
415
if (!strncasecmp(filename, szBasename, iBaseLen))
417
const char* p = filename + iBaseLen;
420
for (p++; *p && strchr("0123456789", *p); p++) ;
423
debug("Found splitted fragment %s", filename);
425
char fullfilename[1024];
426
snprintf(fullfilename, 1024, "%s%c%s", szDirectory, PATH_SEPARATOR, filename);
427
fullfilename[1024-1] = '\0';
429
CommandLine::ExtraFile extrafile(fullfilename, Util::FileSize(fullfilename));
430
extrafiles.push_back(extrafile);
436
bool bFragmentsAdded = false;
438
if (!extrafiles.empty())
440
m_iExtraFiles = extrafiles.size();
441
m_bVerifyingExtraFiles = true;
442
bFragmentsAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles);
443
m_bVerifyingExtraFiles = false;
446
return bFragmentsAdded;
449
void ParChecker::signal_filename(std::string str)
451
const char* szStageMessage[] = { "Loading file", "Verifying file", "Repairing file", "Verifying repaired file" };
453
if (m_eStage == ptRepairing)
455
m_eStage = ptVerifyingRepaired;
458
info("%s %s", szStageMessage[m_eStage], str.c_str());
460
snprintf(m_szProgressLabel, 1024, "%s %s", szStageMessage[m_eStage], str.c_str());
461
m_szProgressLabel[1024-1] = '\0';
466
void ParChecker::signal_progress(double progress)
468
m_iFileProgress = (int)progress;
470
if (m_eStage == ptRepairing)
472
// calculating repair-data for all files
473
m_iStageProgress = m_iFileProgress;
477
// processing individual files
480
if (m_eStage == ptVerifyingRepaired)
482
// repairing individual files
483
iTotalFiles = m_iFilesToRepair;
487
// verifying individual files
488
iTotalFiles = ((Repairer*)m_pRepairer)->sourcefiles.size() + m_iExtraFiles;
493
if (m_iFileProgress < 1000)
495
m_iStageProgress = (m_iProcessedFiles * 1000 + m_iFileProgress) / iTotalFiles;
499
m_iStageProgress = m_iProcessedFiles * 1000 / iTotalFiles;
504
m_iStageProgress = 0;
508
debug("Current-progres: %i, Total-progress: %i", m_iFileProgress, m_iStageProgress);
513
void ParChecker::signal_done(std::string str, int available, int total)
517
if (m_eStage == ptVerifyingSources)
519
if (available < total && !m_bVerifyingExtraFiles)
521
bool bFileExists = true;
523
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
524
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
526
Par2RepairerSourceFile *sourcefile = *it;
527
if (sourcefile && !strcmp(str.c_str(), Util::BaseFileName(sourcefile->TargetFileName().c_str())) &&
528
!sourcefile->GetTargetExists())
537
warn("File %s has %i bad block(s) of total %i block(s)", str.c_str(), total - available, total);
541
warn("File %s with %i block(s) is missing", str.c_str(), total);
547
void ParChecker::Cancel()
549
#ifdef HAVE_PAR2_CANCEL
550
((Repairer*)m_pRepairer)->cancelled = true;
553
error("Could not cancel par-repair. The used version of libpar2 does not support the cancelling of par-repair. Libpar2 needs to be patched for that feature to work.");