#include "checksumdlg.h" #include "../krusader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../krservices.h" #include #include #include #include class CS_Tool; // forward typedef void PREPARE_PROC_FUNC(TDEProcess& proc, CS_Tool *self, const TQStringList& files, const TQString checksumFile, bool recursive, const TQString& stdoutFileName, const TQString& stderrFileName, const TQString& type=TQString()); typedef TQStringList GET_FAILED_FUNC(const TQStringList& stdOut, const TQStringList& stdErr); class CS_Tool { public: enum Type { MD5=0, SHA1, SHA256, TIGER, WHIRLPOOL, SFV, CRC, SHA224, SHA384, SHA512, NumOfTypes }; Type type; TQString binary; bool recursive; bool standardFormat; PREPARE_PROC_FUNC *create, *verify; GET_FAILED_FUNC *failed; }; class CS_ToolByType { public: TQPtrList tools, r_tools; // normal and recursive tools }; // handles md5sum and sha1sum void sumCreateFunc(TDEProcess& proc, CS_Tool *self, const TQStringList& files, const TQString, bool recursive, const TQString& stdoutFileName, const TQString& stderrFileName, const TQString&) { proc.setUseShell(true, "/bin/bash"); proc << KrServices::fullPathName( self->binary ); Q_ASSERT(!recursive); proc << files << "1>" << stdoutFileName << "2>" << stderrFileName; } void sumVerifyFunc(TDEProcess& proc, CS_Tool *self, const TQStringList& /* files */, const TQString checksumFile, bool recursive, const TQString& stdoutFileName, const TQString& stderrFileName, const TQString& /* type */) { proc.setUseShell(true, "/bin/bash"); proc << KrServices::fullPathName( self->binary ); Q_ASSERT(!recursive); proc << "-c" << checksumFile << "1>" << stdoutFileName << "2>" << stderrFileName; } TQStringList sumFailedFunc(const TQStringList& stdOut, const TQStringList& stdErr) { // md5sum and sha1sum print "...: FAILED" for failed files and display // the number of failures to stderr. so if stderr is empty, we'll assume all is ok TQStringList result; if (stdErr.size()==0) return result; result += stdErr; // grep for the ":FAILED" substring const TQString tmp = TQString(": FAILED").local8Bit(); for (uint i=0; ibinary ); if (recursive) proc << "-r"; proc << "-l" << files << "1>" << stdoutFileName << "2>" << stderrFileName; } void deepVerifyFunc(TDEProcess& proc, CS_Tool *self, const TQStringList& files, const TQString checksumFile, bool recursive, const TQString& stdoutFileName, const TQString& stderrFileName, const TQString&) { proc.setUseShell(true, "/bin/bash"); proc << KrServices::fullPathName( self->binary ); if (recursive) proc << "-r"; proc << "-x" << checksumFile << files << "1>" << stdoutFileName << "2>" << stderrFileName; } TQStringList deepFailedFunc(const TQStringList& stdOut, const TQStringList&/* stdErr */) { // *deep dumps (via -x) all failed hashes to stdout return stdOut; } // handles cfv binary void cfvCreateFunc(TDEProcess& proc, CS_Tool *self, const TQStringList& files, const TQString, bool recursive, const TQString& stdoutFileName, const TQString& stderrFileName, const TQString& type) { proc.setUseShell(true, "/bin/bash"); proc << KrServices::fullPathName( self->binary ) << "-C" << "-VV"; if (recursive) proc << "-rr"; proc << "-t" << type << "-f-" << "-U" << files << "1>" << stdoutFileName << "2>" << stderrFileName; } void cfvVerifyFunc(TDEProcess& proc, CS_Tool *self, const TQStringList& /* files */, const TQString checksumFile, bool recursive, const TQString& stdoutFileName, const TQString& stderrFileName, const TQString&type) { proc.setUseShell(true, "/bin/bash"); proc << KrServices::fullPathName( self->binary ) << "-M"; if (recursive) proc << "-rr"; proc << "-U" << "-VV" << "-t" << type << "-f" << checksumFile << "1>" << stdoutFileName << "2>" << stderrFileName;// << files; } TQStringList cfvFailedFunc(const TQStringList& /* stdOut */, const TQStringList& stdErr) { // cfv dumps all failed hashes to stderr return stdErr; } // important: this table should be ordered like so that all md5 tools should be // one after another, and then all sha1 and so on and so forth. they tools must be grouped, // since the code in getTools() counts on it! CS_Tool cs_tools[] = { // type binary recursive stdFmt create_func verify_func failed_func {CS_Tool::MD5, "md5sum", false, true, sumCreateFunc, sumVerifyFunc, sumFailedFunc}, {CS_Tool::MD5, "md5deep", true, true, deepCreateFunc, deepVerifyFunc, deepFailedFunc}, {CS_Tool::MD5, "cfv", true, true, cfvCreateFunc, cfvVerifyFunc, cfvFailedFunc}, {CS_Tool::SHA1, "sha1sum", false, true, sumCreateFunc, sumVerifyFunc, sumFailedFunc}, {CS_Tool::SHA1, "sha1deep", true, true, deepCreateFunc, deepVerifyFunc, deepFailedFunc}, {CS_Tool::SHA1, "cfv", true, true, cfvCreateFunc, cfvVerifyFunc, cfvFailedFunc}, {CS_Tool::SHA224, "sha224sum", false, true, sumCreateFunc, sumVerifyFunc, sumFailedFunc}, {CS_Tool::SHA256, "sha256sum", false, true, sumCreateFunc, sumVerifyFunc, sumFailedFunc}, {CS_Tool::SHA256, "sha256deep", true, true, deepCreateFunc, deepVerifyFunc, deepFailedFunc}, {CS_Tool::SHA384, "sha384sum", false, true, sumCreateFunc, sumVerifyFunc, sumFailedFunc}, {CS_Tool::SHA512, "sha512sum", false, true, sumCreateFunc, sumVerifyFunc, sumFailedFunc}, {CS_Tool::TIGER, "tigerdeep", true, true, deepCreateFunc, deepVerifyFunc, deepFailedFunc}, {CS_Tool::WHIRLPOOL, "whirlpooldeep", true, true, deepCreateFunc, deepVerifyFunc, deepFailedFunc}, {CS_Tool::SFV, "cfv", true, false, cfvCreateFunc, cfvVerifyFunc, cfvFailedFunc}, {CS_Tool::CRC, "cfv", true, false, cfvCreateFunc, cfvVerifyFunc, cfvFailedFunc}, }; TQMap cs_textToType; TQMap cs_typeToText; void initChecksumModule() { // prepare the dictionaries - pity it has to be manually cs_textToType["md5"]=CS_Tool::MD5; cs_textToType["sha1"]=CS_Tool::SHA1; cs_textToType["sha256"]=CS_Tool::SHA256; cs_textToType["sha224"]=CS_Tool::SHA224; cs_textToType["sha384"]=CS_Tool::SHA384; cs_textToType["sha512"]=CS_Tool::SHA512; cs_textToType["tiger"]=CS_Tool::TIGER; cs_textToType["whirlpool"]=CS_Tool::WHIRLPOOL; cs_textToType["sfv"]=CS_Tool::SFV; cs_textToType["crc"]=CS_Tool::CRC; cs_typeToText[CS_Tool::MD5]="md5"; cs_typeToText[CS_Tool::SHA1]="sha1"; cs_typeToText[CS_Tool::SHA256]="sha256"; cs_typeToText[CS_Tool::SHA224]="sha224"; cs_typeToText[CS_Tool::SHA384]="sha384"; cs_typeToText[CS_Tool::SHA512]="sha512"; cs_typeToText[CS_Tool::TIGER]="tiger"; cs_typeToText[CS_Tool::WHIRLPOOL]="whirlpool"; cs_typeToText[CS_Tool::SFV]="sfv"; cs_typeToText[CS_Tool::CRC]="crc"; // build the checksumFilter (for usage in KRQuery) TQMap::Iterator it; for (it=cs_textToType.begin(); it!=cs_textToType.end(); ++it) MatchChecksumDlg::checksumTypesFilter += ("*."+it.key()+" "); } // -------------------------------------------------- // returns a list of tools which can work with recursive or non-recursive mode and are installed // note: only 1 tool from each type is suggested static TQPtrList getTools(bool folders) { TQPtrList result; uint i; for (i=0; i < sizeof(cs_tools)/sizeof(CS_Tool); ++i) { if (result.last() && result.last()->type == cs_tools[i].type) continue; // 1 from each type please if (folders && !cs_tools[i].recursive) continue; if (KrServices::cmdExist(cs_tools[i].binary)) result.append(&cs_tools[i]); } return result; } // ------------- CreateChecksumDlg CreateChecksumDlg::CreateChecksumDlg(const TQStringList& files, bool containFolders, const TQString& path): KDialogBase(Plain, i18n("Create Checksum"), Ok | Cancel, Ok, krApp) { TQPtrList tools = getTools(containFolders); if (tools.count() == 0) { // nothing was suggested?! TQString error = i18n("Can't calculate checksum since no supported tool was found. " "Please check the Dependencies page in Krusader's settings."); if (containFolders) error += i18n("Note: you've selected directories, and probably have no recursive checksum tool installed." " Krusader currently supports md5deep, sha1deep, sha256deep, tigerdeep and cfv"); KMessageBox::error(0, error); return; } TQGridLayout *layout = new TQGridLayout( plainPage(), 1, 1, KDialogBase::marginHint(), KDialogBase::spacingHint()); int row=0; // title (icon+text) TQHBoxLayout *hlayout = new TQHBoxLayout(layout, KDialogBase::spacingHint()); TQLabel *p = new TQLabel(plainPage()); p->setPixmap(krLoader->loadIcon("binary", TDEIcon::Desktop, 32)); hlayout->addWidget(p); TQLabel *l1 = new TQLabel(i18n("About to calculate checksum for the following files") + (containFolders ? i18n(" and folders:") : ":"), plainPage()); hlayout->addWidget(l1); layout->addMultiCellLayout(hlayout, row, row, 0, 1, TQt::AlignLeft); ++row; // file list TDEListBox *lb = new TDEListBox(plainPage()); lb->insertStringList(files); layout->addMultiCellWidget(lb, row, row, 0, 1); ++row; // checksum method TQHBoxLayout *hlayout2 = new TQHBoxLayout(layout, KDialogBase::spacingHint()); TQLabel *l2 = new TQLabel(i18n("Select the checksum method:"), plainPage()); hlayout2->addWidget(l2); KComboBox *method = new KComboBox(plainPage()); // -- fill the combo with available methods uint i; for ( i=0; iinsertItem( cs_typeToText[tools.at(i)->type], i); method->setFocus(); hlayout2->addWidget(method); layout->addMultiCellLayout(hlayout2, row, row, 0, 1, TQt::AlignLeft); ++row; if (exec() != Accepted) return; // else implied: run the process tmpOut = new KTempFile(locateLocal("tmp", "krusader"), ".stdout" ); tmpErr = new KTempFile(locateLocal("tmp", "krusader"), ".stderr" ); TDEProcess proc; CS_Tool *mytool = tools.at(method->currentItem()); mytool->create(proc, mytool, KrServices::quote(files), TQString(), containFolders, tmpOut->name(), tmpErr->name(), method->currentText()); krApp->startWaiting(i18n("Calculating checksums ..."), 0, true); TQApplication::setOverrideCursor( KCursor::waitCursor() ); bool r = proc.start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput); if (r) while ( proc.isRunning() ) { usleep( 500 ); tqApp->processEvents(); if (krApp->wasWaitingCancelled()) { // user cancelled proc.kill(); TQApplication::restoreOverrideCursor(); return; } }; krApp->stopWait(); TQApplication::restoreOverrideCursor(); if (!r || !proc.normalExit()) { KMessageBox::error(0, i18n("There was an error while running %1.").arg(mytool->binary)); return; } // suggest a filename TQString suggestedFilename = path + '/'; if (files.count() > 1) suggestedFilename += ("checksum." + cs_typeToText[mytool->type]); else suggestedFilename += (files[0] + '.' + cs_typeToText[mytool->type]); // send both stdout and stderr TQStringList stdOut, stdErr; if (!KrServices::fileToStringList(tmpOut->textStream(), stdOut) || !KrServices::fileToStringList(tmpErr->textStream(), stdErr)) { KMessageBox::error(krApp, i18n("Error reading stdout or stderr")); return; } ChecksumResultsDlg dlg( stdOut, stdErr, suggestedFilename, mytool->binary, cs_typeToText[mytool->type], mytool->standardFormat); tmpOut->unlink(); delete tmpOut; tmpErr->unlink(); delete tmpErr; } // ------------- MatchChecksumDlg TQString MatchChecksumDlg::checksumTypesFilter; MatchChecksumDlg::MatchChecksumDlg(const TQStringList& files, bool containFolders, const TQString& path, const TQString& checksumFile): KDialogBase(Plain, i18n("Verify Checksum"), Ok | Cancel, Ok, krApp) { TQPtrList tools = getTools(containFolders); if (tools.count() == 0) { // nothing was suggested?! TQString error = i18n("Can't verify checksum since no supported tool was found. " "Please check the Dependencies page in Krusader's settings."); if (containFolders) error += i18n("Note: you've selected directories, and probably have no recursive checksum tool installed." " Krusader currently supports md5deep, sha1deep, sha256deep, tigerdeep and cfv"); KMessageBox::error(0, error); return; } TQGridLayout *layout = new TQGridLayout( plainPage(), 1, 1, KDialogBase::marginHint(), KDialogBase::spacingHint()); int row=0; // title (icon+text) TQHBoxLayout *hlayout = new TQHBoxLayout(layout, KDialogBase::spacingHint()); TQLabel *p = new TQLabel(plainPage()); p->setPixmap(krLoader->loadIcon("binary", TDEIcon::Desktop, 32)); hlayout->addWidget(p); TQLabel *l1 = new TQLabel(i18n("About to verify checksum for the following files") + (containFolders ? i18n(" and folders:") : ":"), plainPage()); hlayout->addWidget(l1); layout->addMultiCellLayout(hlayout, row, row, 0, 1, TQt::AlignLeft); ++row; // file list TDEListBox *lb = new TDEListBox(plainPage()); lb->insertStringList(files); layout->addMultiCellWidget(lb, row, row, 0, 1); ++row; // checksum file TQHBoxLayout *hlayout2 = new TQHBoxLayout(layout, KDialogBase::spacingHint()); TQLabel *l2 = new TQLabel(i18n("Checksum file:"), plainPage()); hlayout2->addWidget(l2); KURLRequester *checksumFileReq = new KURLRequester( plainPage() ); if (!checksumFile.isEmpty()) checksumFileReq->setURL(checksumFile); checksumFileReq->fileDialog()->setURL(path); checksumFileReq->setFocus(); hlayout2->addWidget(checksumFileReq); layout->addMultiCellLayout(hlayout2, row, row, 0, 1, TQt::AlignLeft); if (exec() != Accepted) return; TQString file = checksumFileReq->url(); TQString extension; if (!verifyChecksumFile(file, extension)) { KMessageBox::error(0, i18n("Error reading checksum file %1.
Please specify a valid checksum file.
").arg(file)); return; } // do we have a tool for that extension? uint i; CS_Tool *mytool = 0; for ( i=0; i < tools.count(); ++i ) if (cs_typeToText[tools.at(i)->type] == extension.lower()) { mytool = tools.at(i); break; } if (!mytool) { KMessageBox::error(0, i18n("Krusader can't find a checksum tool that handles %1 on your system. Please check the Dependencies page in Krusader's settings.").arg(extension)); return; } // else implied: run the process tmpOut = new KTempFile(locateLocal("tmp", "krusader"), ".stdout" ); tmpErr = new KTempFile(locateLocal("tmp", "krusader"), ".stderr" ); TDEProcess proc; mytool->verify(proc, mytool, KrServices::quote(files), KrServices::quote(file), containFolders, tmpOut->name(), tmpErr->name(), extension); krApp->startWaiting(i18n("Verifying checksums ..."), 0, true); TQApplication::setOverrideCursor( KCursor::waitCursor() ); bool r = proc.start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput); if (r) while ( proc.isRunning() ) { usleep( 500 ); tqApp->processEvents(); if (krApp->wasWaitingCancelled()) { // user cancelled proc.kill(); TQApplication::restoreOverrideCursor(); return; } }; if (!r || !proc.normalExit()) { KMessageBox::error(0, i18n("There was an error while running %1.").arg(mytool->binary)); return; } TQApplication::restoreOverrideCursor(); krApp->stopWait(); // send both stdout and stderr TQStringList stdOut,stdErr; if (!KrServices::fileToStringList(tmpOut->textStream(), stdOut) || !KrServices::fileToStringList(tmpErr->textStream(), stdErr)) { KMessageBox::error(krApp, i18n("Error reading stdout or stderr")); return; } VerifyResultDlg dlg(mytool->failed(stdOut, stdErr)); tmpOut->unlink(); delete tmpOut; tmpErr->unlink(); delete tmpErr; } bool MatchChecksumDlg::verifyChecksumFile(TQString path, TQString& extension) { TQFileInfo f(path); if (!f.exists() || f.isDir()) return false; // find the extension extension = path.mid(path.findRev(".")+1); // TODO: do we know the extension? if not, ask the user for one return true; } // ------------- VerifyResultDlg VerifyResultDlg::VerifyResultDlg(const TQStringList& failed): KDialogBase(Plain, i18n("Verify Checksum"), Close, Close, krApp) { TQGridLayout *layout = new TQGridLayout( plainPage(), 1, 1, KDialogBase::marginHint(), KDialogBase::spacingHint()); bool errors = failed.size()>0; int row = 0; // create the icon and title TQHBoxLayout *hlayout = new TQHBoxLayout(layout, KDialogBase::spacingHint()); TQLabel p(plainPage()); p.setPixmap(krLoader->loadIcon(errors ? "messagebox_critical" : "messagebox_info", TDEIcon::Desktop, 32)); hlayout->addWidget(&p); TQLabel *l1 = new TQLabel((errors ? i18n("Errors were detected while verifying the checksums") : i18n("Checksums were verified successfully")), plainPage()); hlayout->addWidget(l1); layout->addMultiCellLayout(hlayout,row,row,0,1, TQt::AlignLeft); ++row; if (errors) { TQLabel *l3 = new TQLabel(i18n("The following files have failed:"), plainPage()); layout->addMultiCellWidget(l3, row, row, 0, 1); ++row; TDEListBox *lb2 = new TDEListBox(plainPage()); lb2->insertStringList(failed); layout->addMultiCellWidget(lb2, row, row, 0, 1); ++row; } exec(); } // ------------- ChecksumResultsDlg ChecksumResultsDlg::ChecksumResultsDlg(const TQStringList& stdOut, const TQStringList& stdErr, const TQString& suggestedFilename, const TQString& binary, const TQString& /* type */, bool standardFormat): KDialogBase(Plain, i18n("Create Checksum"), Ok | Cancel, Ok, krApp), _binary(binary) { TQGridLayout *layout = new TQGridLayout( plainPage(), 1, 1, KDialogBase::marginHint(), KDialogBase::spacingHint()); // md5 tools display errors into stderr, so we'll use that to determine the result of the job bool errors = stdErr.size()>0; bool successes = stdOut.size()>0; int row = 0; // create the icon and title TQHBoxLayout *hlayout = new TQHBoxLayout(layout, KDialogBase::spacingHint()); TQLabel p(plainPage()); p.setPixmap(krLoader->loadIcon(errors ? "messagebox_critical" : "messagebox_info", TDEIcon::Desktop, 32)); hlayout->addWidget(&p); TQLabel *l1 = new TQLabel((errors ? i18n("Errors were detected while creating the checksums") : i18n("Checksums were created successfully")), plainPage()); hlayout->addWidget(l1); layout->addMultiCellLayout(hlayout,row,row,0,1, TQt::AlignLeft); ++row; if (successes) { if (errors) { TQLabel *l2 = new TQLabel(i18n("Here are the calculated checksums:"), plainPage()); layout->addMultiCellWidget(l2, row, row, 0, 1); ++row; } TDEListView *lv = new TDEListView(plainPage()); if(standardFormat){ lv->addColumn(i18n("Hash")); lv->addColumn(i18n("File")); lv->setAllColumnsShowFocus(true); } else { lv->addColumn(i18n("File and hash")); } for ( TQStringList::ConstIterator it = stdOut.begin(); it != stdOut.end(); ++it ) { TQString line = (*it); if(standardFormat) { int space = line.find(' '); new TDEListViewItem(lv, line.left(space), line.mid(space+2)); } else { new TDEListViewItem(lv, line); } } layout->addMultiCellWidget(lv, row, row, 0, 1); ++row; } if (errors) { TQFrame *line1 = new TQFrame( plainPage() ); line1->setGeometry( TQRect( 60, 210, 501, 20 ) ); line1->setFrameShape( TQFrame::HLine ); line1->setFrameShadow( TQFrame::Sunken ); layout->addMultiCellWidget(line1, row, row, 0, 1); ++row; TQLabel *l3 = new TQLabel(i18n("Here are the errors received:"), plainPage()); layout->addMultiCellWidget(l3, row, row, 0, 1); ++row; TDEListBox *lb = new TDEListBox(plainPage()); lb->insertStringList(stdErr); layout->addMultiCellWidget(lb, row, row, 0, 1); ++row; } // save checksum to disk, if any hashes are found KURLRequester *checksumFile=0; TQCheckBox *saveFileCb=0; if (successes) { TQHBoxLayout *hlayout2 = new TQHBoxLayout(layout, KDialogBase::spacingHint()); saveFileCb = new TQCheckBox(i18n("Save checksum to file:"), plainPage()); saveFileCb->setChecked(true); hlayout2->addWidget(saveFileCb); checksumFile = new KURLRequester( suggestedFilename, plainPage() ); hlayout2->addWidget(checksumFile, TQt::AlignLeft); layout->addMultiCellLayout(hlayout2, row, row,0,1, TQt::AlignLeft); ++row; connect(saveFileCb, TQT_SIGNAL(toggled(bool)), checksumFile, TQT_SLOT(setEnabled(bool))); checksumFile->setFocus(); } TQCheckBox *onePerFile=0; if (stdOut.size() > 1 && standardFormat) { onePerFile = new TQCheckBox(i18n("Checksum file for each source file"), plainPage()); onePerFile->setChecked(false); // clicking this, disables the 'save as' part connect(onePerFile, TQT_SIGNAL(toggled(bool)), saveFileCb, TQT_SLOT(toggle())); connect(onePerFile, TQT_SIGNAL(toggled(bool)), saveFileCb, TQT_SLOT(setDisabled(bool))); connect(onePerFile, TQT_SIGNAL(toggled(bool)), checksumFile, TQT_SLOT(setDisabled(bool))); layout->addMultiCellWidget(onePerFile, row, row,0,1, TQt::AlignLeft); ++row; } if (exec() == Accepted && successes) { if (stdOut.size()>1 && standardFormat && onePerFile->isChecked()) { savePerFile(stdOut, suggestedFilename.mid(suggestedFilename.findRev('.'))); } else if (saveFileCb->isEnabled() && saveFileCb->isChecked() && !checksumFile->url().simplifyWhiteSpace().isEmpty()) { saveChecksum(stdOut, checksumFile->url()); } } } bool ChecksumResultsDlg::saveChecksum(const TQStringList& data, TQString filename) { if (TQFile::exists(filename) && KMessageBox::warningContinueCancel(this, i18n("File %1 already exists.\nAre you sure you want to overwrite it?").arg(filename), i18n("Warning"), i18n("Overwrite")) != KMessageBox::Continue) { // find a better name to save to filename = KFileDialog::getSaveFileName(TQString(), "*", 0, i18n("Select a file to save to")); if (filename.simplifyWhiteSpace().isEmpty()) return false; } TQFile file(filename); if (!file.open(IO_WriteOnly)) { KMessageBox::detailedError(0, i18n("Error saving file %1").arg(filename), file.errorString()); return false; } TQTextStream stream(&file); for ( TQStringList::ConstIterator it = data.constBegin(); it != data.constEnd(); ++it) stream << *it << "\n"; file.close(); return true; } void ChecksumResultsDlg::savePerFile(const TQStringList& data, const TQString& type) { krApp->startWaiting(i18n("Saving checksum files..."), 0); for ( TQStringList::ConstIterator it = data.begin(); it != data.end(); ++it ) { TQString line = (*it); TQString filename = line.mid(line.find(' ')+2)+type; if (!saveChecksum(*it, filename)) { KMessageBox::error(0, i18n("Errors occured while saving multiple checksums. Stopping")); krApp->stopWait(); return; } } krApp->stopWait(); }