File indexing completed on 2024-05-12 04:51:04

0001 /*
0002     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "k3bdatajob.h"
0007 #include "k3bdatadoc.h"
0008 #include "k3bisoimager.h"
0009 #include "k3bdatamultisessionparameterjob.h"
0010 #include "k3bchecksumpipe.h"
0011 #include "k3bcore.h"
0012 #include "k3bglobals.h"
0013 #include "k3bversion.h"
0014 #include "k3bdevice.h"
0015 #include "k3bdevicehandler.h"
0016 #include "k3btoc.h"
0017 #include "k3btrack.h"
0018 #include "k3bexternalbinmanager.h"
0019 #include "k3bcdrecordwriter.h"
0020 #include "k3bcdrdaowriter.h"
0021 #include "k3bglobalsettings.h"
0022 #include "k3bactivepipe.h"
0023 #include "k3bfilesplitter.h"
0024 #include "k3bverificationjob.h"
0025 #include "k3biso9660.h"
0026 #include "k3bisooptions.h"
0027 #include "k3bdeviceglobals.h"
0028 #include "k3bgrowisofswriter.h"
0029 #include "k3b_i18n.h"
0030 
0031 #include <KIO/Global>
0032 #include <KIO/Job>
0033 
0034 #include <QDataStream>
0035 #include <QDateTime>
0036 #include <QDebug>
0037 #include <QFile>
0038 #include <QString>
0039 #include <QStringList>
0040 #include <QTemporaryFile>
0041 
0042 
0043 
0044 class K3b::DataJob::Private
0045 {
0046 public:
0047     Private()
0048         : usedWritingApp(K3b::WritingAppAuto),
0049           verificationJob( 0 ),
0050           pipe( 0 ) {
0051     }
0052 
0053     K3b::DataDoc* doc;
0054 
0055     bool initializingImager;
0056     bool imageFinished;
0057     bool canceled;
0058 
0059     QTemporaryFile* tocFile;
0060 
0061     int usedDataMode;
0062     K3b::WritingApp usedWritingApp;
0063     K3b::WritingMode usedWritingMode;
0064 
0065     int copies;
0066     int copiesDone;
0067 
0068     K3b::VerificationJob* verificationJob;
0069 
0070     K3b::FileSplitter imageFile;
0071     K3b::ActivePipe* pipe;
0072 
0073     K3b::DataMultiSessionParameterJob* multiSessionParameterJob;
0074 
0075     QByteArray checksumCache;
0076 };
0077 
0078 
0079 K3b::DataJob::DataJob( K3b::DataDoc* doc, K3b::JobHandler* hdl, QObject* parent )
0080     : K3b::BurnJob( hdl, parent )
0081 {
0082     d = new Private;
0083     d->multiSessionParameterJob = new K3b::DataMultiSessionParameterJob( doc, this, this );
0084     connectSubJob( d->multiSessionParameterJob,
0085                    SLOT(slotMultiSessionParamterSetupDone(bool)),
0086                    SIGNAL(newTask(QString)),
0087                    SIGNAL(newSubTask(QString)) );
0088 
0089     d->doc = doc;
0090     m_writerJob = 0;
0091     d->tocFile = 0;
0092     m_isoImager = 0;
0093 }
0094 
0095 
0096 K3b::DataJob::~DataJob()
0097 {
0098     qDebug();
0099     delete d->pipe;
0100     delete d->tocFile;
0101     delete d;
0102 }
0103 
0104 
0105 K3b::Doc* K3b::DataJob::doc() const
0106 {
0107     return d->doc;
0108 }
0109 
0110 
0111 K3b::Device::Device* K3b::DataJob::writer() const
0112 {
0113     if( doc()->onlyCreateImages() )
0114         return 0; // no writer needed -> no blocking on K3b::BurnJob
0115     else
0116         return doc()->burner();
0117 }
0118 
0119 
0120 void K3b::DataJob::start()
0121 {
0122     qDebug();
0123     jobStarted();
0124 
0125     d->canceled = false;
0126     d->imageFinished = false;
0127     d->copies = d->doc->copies();
0128     d->copiesDone = 0;
0129 
0130     prepareImager();
0131 
0132     if( d->doc->dummy() ) {
0133         d->doc->setVerifyData( false );
0134         d->copies = 1;
0135     }
0136 
0137     emit newTask( i18n("Preparing data") );
0138 
0139     // there is no harm in setting these even if we write on-the-fly
0140     d->imageFile.setName( d->doc->tempDir() );
0141 
0142     d->multiSessionParameterJob->start();
0143 }
0144 
0145 
0146 void K3b::DataJob::slotMultiSessionParamterSetupDone( bool success )
0147 {
0148     qDebug() << success;
0149     if ( success ) {
0150         prepareWriting();
0151     }
0152     else {
0153         if ( d->multiSessionParameterJob->hasBeenCanceled() ) {
0154             emit canceled();
0155         }
0156         cleanup();
0157         jobFinished( false );
0158     }
0159 }
0160 
0161 
0162 void K3b::DataJob::prepareWriting()
0163 {
0164     qDebug();
0165     if( !d->doc->onlyCreateImages() &&
0166         ( d->multiSessionParameterJob->usedMultiSessionMode() == K3b::DataDoc::CONTINUE ||
0167           d->multiSessionParameterJob->usedMultiSessionMode() == K3b::DataDoc::FINISH ) ) {
0168         unsigned int nextSessionStart = d->multiSessionParameterJob->nextSessionStart();
0169         // for some reason cdrdao needs 150 additional sectors in the ms info
0170         if( writingApp() == K3b::WritingAppCdrdao ) {
0171             nextSessionStart += 150;
0172         }
0173         m_isoImager->setMultiSessionInfo( QString::asprintf( "%u,%u",
0174                                                              d->multiSessionParameterJob->previousSessionStart(),
0175                                                              nextSessionStart ),
0176                                           d->multiSessionParameterJob->importPreviousSession() ? d->doc->burner() : 0 );
0177     }
0178     else {
0179         m_isoImager->setMultiSessionInfo( QString(), 0 );
0180     }
0181 
0182     d->initializingImager = true;
0183     m_isoImager->init();
0184 }
0185 
0186 
0187 void K3b::DataJob::writeImage()
0188 {
0189     qDebug();
0190     d->initializingImager = false;
0191 
0192     emit burning(false);
0193 
0194     // get image file path
0195     if( d->doc->tempDir().isEmpty() )
0196         d->doc->setTempDir( K3b::findUniqueFilePrefix( d->doc->isoOptions().volumeID() ) + ".iso" );
0197 
0198     // TODO: check if the image file is part of the project and if so warn the user
0199     //       and append some number to make the path unique.
0200 
0201     //
0202     // Check the image file
0203     if( !d->doc->onTheFly() || d->doc->onlyCreateImages() ) {
0204         d->imageFile.setName( d->doc->tempDir() );
0205         if( !d->imageFile.open( QIODevice::WriteOnly ) ) {
0206             emit infoMessage( i18n("Could not open %1 for writing", d->doc->tempDir() ), MessageError );
0207             cleanup();
0208             jobFinished(false);
0209             return;
0210         }
0211     }
0212 
0213     emit newTask( i18n("Creating image file") );
0214     emit newSubTask( i18n("Track 1 of 1") );
0215     emit infoMessage( i18n("Creating image file in %1",d->doc->tempDir()), MessageInfo );
0216 
0217     m_isoImager->start();
0218     startPipe();
0219 }
0220 
0221 
0222 void K3b::DataJob::startPipe()
0223 {
0224     qDebug();
0225     //
0226     // Open the active pipe which does the streaming
0227     //
0228     delete d->pipe;
0229     if ( d->imageFinished || !d->doc->verifyData() )
0230         d->pipe = new K3b::ActivePipe();
0231     else
0232         d->pipe = new K3b::ChecksumPipe();
0233 
0234 #ifdef __GNUC__
0235 #warning Growisofs needs stdin to be closed in order to exit gracefully. Cdrecord does not. However,  if closed with cdrecord we loose parts of stderr. Why?
0236 #endif
0237     if( d->imageFinished || ( d->doc->onTheFly() && !d->doc->onlyCreateImages() ) )
0238         d->pipe->writeTo( m_writerJob->ioDevice(), d->usedWritingApp != K3b::WritingAppCdrecord );
0239     else
0240         d->pipe->writeTo( &d->imageFile, true );
0241 
0242     if ( d->imageFinished )
0243         d->pipe->readFrom( &d->imageFile, true );
0244     else
0245         d->pipe->readFrom( m_isoImager->ioDevice(), true );
0246 
0247     d->pipe->open( true );
0248 }
0249 
0250 
0251 bool K3b::DataJob::startOnTheFlyWriting()
0252 {
0253     qDebug();
0254     if( prepareWriterJob() ) {
0255         if( startWriterJob() ) {
0256             d->initializingImager = false;
0257             m_isoImager->start();
0258             startPipe();
0259             return true;
0260         }
0261     }
0262     return false;
0263 }
0264 
0265 
0266 void K3b::DataJob::cancel()
0267 {
0268     qDebug();
0269 
0270     emit canceled();
0271 
0272     d->canceled = true;
0273 
0274     //
0275     // Just cancel all and return, let slotMultiSessionParamterSetupDone,
0276     // slotIsoImagerFinished, and slotWriterJobFinished take care of the rest
0277     //
0278     if ( active() && !cancelAll() ) {
0279         qDebug() << "cancellation already done";
0280         cleanup();
0281         jobFinished( false );
0282     }
0283 }
0284 
0285 
0286 bool K3b::DataJob::cancelAll()
0287 {
0288     qDebug();
0289     bool somethingCanceled = false;
0290     if ( m_isoImager->active() ) {
0291         qDebug() << "cancelling iso imager";
0292         m_isoImager->cancel();
0293         somethingCanceled = true;
0294     }
0295     if( m_writerJob && m_writerJob->active() ) {
0296         qDebug() << "cancelling writing job";
0297         m_writerJob->cancel();
0298         somethingCanceled = true;
0299     }
0300     if( d->verificationJob && d->verificationJob->active() ) {
0301         qDebug() << "cancelling verification job";
0302         d->verificationJob->cancel();
0303         somethingCanceled = true;
0304     }
0305     if ( d->multiSessionParameterJob && d->multiSessionParameterJob->active() ) {
0306         qDebug() << "cancelling multiSessionParameterJob";
0307         d->multiSessionParameterJob->cancel();
0308         somethingCanceled = true;
0309     }
0310 
0311     qDebug() << somethingCanceled;
0312     return somethingCanceled;
0313 }
0314 
0315 
0316 void K3b::DataJob::slotIsoImagerPercent( int p )
0317 {
0318     if( d->doc->onlyCreateImages() ) {
0319         emit subPercent( p );
0320         emit percent( p );
0321     }
0322     else if( !d->doc->onTheFly() ) {
0323         double totalTasks = d->copies;
0324         double tasksDone = d->copiesDone; // =0 when creating an image
0325         if( d->doc->verifyData() ) {
0326             totalTasks*=2;
0327             tasksDone*=2;
0328         }
0329         if( !d->doc->onTheFly() ) {
0330             totalTasks+=1.0;
0331         }
0332 
0333         emit subPercent( p );
0334         emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
0335     }
0336 }
0337 
0338 
0339 void K3b::DataJob::slotIsoImagerFinished( bool success )
0340 {
0341     qDebug();
0342     if( d->initializingImager ) {
0343         if( success ) {
0344             if( d->doc->onTheFly() && !d->doc->onlyCreateImages() ) {
0345                 if( !startOnTheFlyWriting() ) {
0346                     cleanup();
0347                     jobFinished( false );
0348                 }
0349             }
0350             else {
0351                 writeImage();
0352             }
0353         }
0354         else {
0355             if( m_isoImager->hasBeenCanceled() ) {
0356                 cancel();
0357             }
0358             else if ( !cancelAll() ) {
0359                 cleanup();
0360                 jobFinished( false );
0361             }
0362         }
0363     }
0364     else {
0365         // cache the calculated checksum since the ChecksumPipe may be deleted below
0366         if ( ChecksumPipe* cp = qobject_cast<ChecksumPipe*>( d->pipe ) )
0367             d->checksumCache = cp->checksum();
0368 
0369         if( !d->doc->onTheFly() ||
0370             d->doc->onlyCreateImages() ) {
0371 
0372             if( success ) {
0373                 emit infoMessage( i18n("Image successfully created in %1", d->doc->tempDir()), K3b::Job::MessageSuccess );
0374                 d->imageFinished = true;
0375 
0376                 if( d->doc->onlyCreateImages() ) {
0377                     jobFinished( true );
0378                 }
0379                 else if( !d->imageFile.open( QIODevice::ReadOnly ) ) {
0380                     emit infoMessage( i18n("Could not open file %1", d->doc->tempDir() ), MessageError );
0381                     cleanup();
0382                     jobFinished(false);
0383                 }
0384                 else if( prepareWriterJob() ) {
0385                     startWriterJob();
0386                     startPipe();
0387                 }
0388             }
0389             else {
0390                 if( m_isoImager->hasBeenCanceled() )
0391                     emit canceled();
0392                 else
0393                     emit infoMessage( i18n("Error while creating ISO image"), MessageError );
0394 
0395                 cancelAll();
0396                 cleanup();
0397                 jobFinished( false );
0398             }
0399         }
0400         else { // on-the-fly
0401             if( success ) {
0402                 if ( !m_writerJob->active() )
0403                     finishCopy();
0404             }
0405             else {
0406                 //
0407                 // In case the imager failed let's make sure the writer does not emit an unusable
0408                 // error message.
0409                 //
0410                 if( m_writerJob && m_writerJob->active() )
0411                     m_writerJob->setSourceUnreadable( true );
0412 
0413                 // there is one special case which we need to handle here: the iso imager might be canceled
0414                 // FIXME: the iso imager should not be able to cancel itself
0415                 if( m_isoImager->hasBeenCanceled() && !this->hasBeenCanceled() )
0416                     cancel();
0417             }
0418         }
0419     }
0420 }
0421 
0422 
0423 bool K3b::DataJob::startWriterJob()
0424 {
0425     qDebug();
0426     if( d->doc->dummy() )
0427         emit newTask( i18n("Simulating") );
0428     else if( d->copies > 1 )
0429         emit newTask( i18n("Writing Copy %1",d->copiesDone+1) );
0430     else
0431         emit newTask( i18n("Writing") );
0432 
0433     emit burning(true);
0434     m_writerJob->start();
0435     return true;
0436 }
0437 
0438 
0439 void K3b::DataJob::slotWriterJobPercent( int p )
0440 {
0441     double totalTasks = d->copies;
0442     double tasksDone = d->copiesDone;
0443     if( d->doc->verifyData() ) {
0444         totalTasks*=2;
0445         tasksDone*=2;
0446     }
0447     if( !d->doc->onTheFly() ) {
0448         totalTasks+=1.0;
0449         tasksDone+=1.0;
0450     }
0451 
0452     emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
0453 }
0454 
0455 
0456 void K3b::DataJob::slotWriterNextTrack( int t, int tt )
0457 {
0458     emit newSubTask( i18n("Writing Track %1 of %2",t,tt) );
0459 }
0460 
0461 
0462 void K3b::DataJob::slotWriterJobFinished( bool success )
0463 {
0464     qDebug();
0465 
0466     if( success ) {
0467         if ( !d->doc->onTheFly() ||
0468              !m_isoImager->active() ) {
0469             finishCopy();
0470         }
0471     }
0472     else {
0473         if ( !cancelAll() ) {
0474             cleanup();
0475             jobFinished( false );
0476         }
0477     }
0478 }
0479 
0480 
0481 void K3b::DataJob::finishCopy()
0482 {
0483     // the writerJob should have emitted the "simulation/writing successful" signal
0484 
0485     if( d->doc->verifyData() ) {
0486         if( !d->verificationJob ) {
0487             d->verificationJob = new K3b::VerificationJob( this, this );
0488             connect( d->verificationJob, SIGNAL(infoMessage(QString,int)),
0489                      this, SIGNAL(infoMessage(QString,int)) );
0490             connect( d->verificationJob, SIGNAL(newTask(QString)),
0491                      this, SIGNAL(newSubTask(QString)) );
0492             connect( d->verificationJob, SIGNAL(newSubTask(QString)),
0493                      this, SIGNAL(newSubTask(QString)) );
0494             connect( d->verificationJob, SIGNAL(percent(int)),
0495                      this, SLOT(slotVerificationProgress(int)) );
0496             connect( d->verificationJob, SIGNAL(percent(int)),
0497                      this, SIGNAL(subPercent(int)) );
0498             connect( d->verificationJob, SIGNAL(finished(bool)),
0499                      this, SLOT(slotVerificationFinished(bool)) );
0500             connect( d->verificationJob, SIGNAL(debuggingOutput(QString,QString)),
0501                      this, SIGNAL(debuggingOutput(QString,QString)) );
0502 
0503         }
0504         d->verificationJob->clear();
0505         d->verificationJob->setDevice( d->doc->burner() );
0506         d->verificationJob->setGrownSessionSize( m_isoImager->size() );
0507         d->verificationJob->addTrack( 0, d->checksumCache, m_isoImager->size() );
0508 
0509         emit burning(false);
0510 
0511         emit newTask( i18n("Verifying written data") );
0512 
0513         d->verificationJob->start();
0514     }
0515     else {
0516         d->copiesDone++;
0517 
0518         if( d->copiesDone < d->copies ) {
0519             if( !K3b::eject( d->doc->burner() ) ) {
0520                 blockingInformation( i18n("K3b was unable to eject the written disk. Please do so manually.") );
0521             }
0522 
0523             bool failed = false;
0524             if( d->doc->onTheFly() )
0525                 failed = !startOnTheFlyWriting();
0526             else
0527                 failed = !prepareWriterJob() || !startWriterJob();
0528 
0529             if( failed ) {
0530                 cancel();
0531             }
0532             else if( !d->doc->onTheFly() ) {
0533 #ifdef __GNUC__
0534 #warning Growisofs needs stdin to be closed in order to exit gracefully. Cdrecord does not. However,  if closed with cdrecord we loose parts of stderr. Why?
0535 #endif
0536                 d->pipe->writeTo( m_writerJob->ioDevice(), d->usedWritingApp != K3b::WritingAppCdrecord );
0537                 d->pipe->open(true);
0538             }
0539         }
0540         else {
0541             cleanup();
0542             if ( k3bcore->globalSettings()->ejectMedia() ) {
0543                 K3b::Device::eject( d->doc->burner() );
0544             }
0545             jobFinished(true);
0546         }
0547     }
0548 }
0549 
0550 
0551 void K3b::DataJob::slotVerificationProgress( int p )
0552 {
0553     double totalTasks = d->copies*2;
0554     double tasksDone = d->copiesDone*2 + 1; // the writing of the current copy has already been finished
0555 
0556     if( !d->doc->onTheFly() ) {
0557         totalTasks+=1.0;
0558         tasksDone+=1.0;
0559     }
0560 
0561     emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
0562 }
0563 
0564 
0565 void K3b::DataJob::slotVerificationFinished( bool success )
0566 {
0567     qDebug();
0568     d->copiesDone++;
0569 
0570     // reconnect our imager which we disconnected for the verification
0571     connectImager();
0572 
0573     if( k3bcore->globalSettings()->ejectMedia() || d->copiesDone < d->copies )
0574         K3b::Device::eject( d->doc->burner() );
0575 
0576     if( !d->canceled && d->copiesDone < d->copies ) {
0577         bool failed = false;
0578         if( d->doc->onTheFly() )
0579             failed = !startOnTheFlyWriting();
0580         else
0581             failed = !prepareWriterJob() || !startWriterJob();
0582 
0583         if( failed )
0584             cancel();
0585         else if( !d->doc->onTheFly() ) {
0586 #ifdef __GNUC__
0587 #warning Growisofs needs stdin to be closed in order to exit gracefully. Cdrecord does not. However,  if closed with cdrecord we loose parts of stderr. Why?
0588 #endif
0589             d->pipe->writeTo( m_writerJob->ioDevice(), d->usedWritingApp != K3b::WritingAppCdrecord );
0590             d->pipe->open(true);
0591         }
0592     }
0593     else {
0594         cleanup();
0595         jobFinished( success );
0596     }
0597 }
0598 
0599 
0600 void K3b::DataJob::setWriterJob( K3b::AbstractWriter* writer )
0601 {
0602     qDebug();
0603     // FIXME: progressedsize for multiple copies
0604     m_writerJob = writer;
0605     connect( m_writerJob, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) );
0606     connect( m_writerJob, SIGNAL(percent(int)), this, SLOT(slotWriterJobPercent(int)) );
0607     connect( m_writerJob, SIGNAL(processedSize(int,int)), this, SIGNAL(processedSize(int,int)) );
0608     connect( m_writerJob, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) );
0609     connect( m_writerJob, SIGNAL(processedSubSize(int,int)), this, SIGNAL(processedSubSize(int,int)) );
0610     connect( m_writerJob, SIGNAL(nextTrack(int,int)), this, SLOT(slotWriterNextTrack(int,int)) );
0611     connect( m_writerJob, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
0612     connect( m_writerJob, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
0613     connect( m_writerJob, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)), this, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)) );
0614     connect( m_writerJob, SIGNAL(finished(bool)), this, SLOT(slotWriterJobFinished(bool)) );
0615     connect( m_writerJob, SIGNAL(newSubTask(QString)), this, SIGNAL(newSubTask(QString)) );
0616     connect( m_writerJob, SIGNAL(debuggingOutput(QString,QString)),
0617              this, SIGNAL(debuggingOutput(QString,QString)) );
0618 }
0619 
0620 
0621 void K3b::DataJob::setImager( K3b::IsoImager* imager )
0622 {
0623     qDebug();
0624     if( m_isoImager != imager ) {
0625         delete m_isoImager;
0626 
0627         m_isoImager = imager;
0628 
0629         connectImager();
0630     }
0631 }
0632 
0633 
0634 void K3b::DataJob::connectImager()
0635 {
0636     qDebug();
0637     m_isoImager->disconnect( this );
0638     connect( m_isoImager, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) );
0639     connect( m_isoImager, SIGNAL(percent(int)), this, SLOT(slotIsoImagerPercent(int)) );
0640     connect( m_isoImager, SIGNAL(finished(bool)), this, SLOT(slotIsoImagerFinished(bool)) );
0641     connect( m_isoImager, SIGNAL(debuggingOutput(QString,QString)),
0642              this, SIGNAL(debuggingOutput(QString,QString)) );
0643 }
0644 
0645 
0646 void K3b::DataJob::prepareImager()
0647 {
0648     qDebug();
0649     if( !m_isoImager )
0650         setImager( new K3b::IsoImager( d->doc, this, this ) );
0651 }
0652 
0653 
0654 bool K3b::DataJob::prepareWriterJob()
0655 {
0656     qDebug();
0657     if( m_writerJob ) {
0658         delete m_writerJob;
0659         m_writerJob = 0;
0660     }
0661 
0662     // if we append a new session we asked for an appendable cd already
0663     if( !waitForBurnMedium() ) {
0664         return false;
0665     }
0666 
0667     // It seems as if cdrecord is not able to append sessions in dao mode whereas cdrdao is
0668     if( d->usedWritingApp == K3b::WritingAppCdrecord )  {
0669         if( !setupCdrecordJob() ) {
0670             return false;
0671         }
0672     }
0673     else if ( d->usedWritingApp == K3b::WritingAppCdrdao ) {
0674         if ( !setupCdrdaoJob() ) {
0675             return false;
0676         }
0677     }
0678     else {
0679         if ( !setupGrowisofsJob() ) {
0680             return false;
0681         }
0682     }
0683 
0684     return true;
0685 }
0686 
0687 
0688 bool K3b::DataJob::waitForBurnMedium()
0689 {
0690     // start with all media types supported by the writer
0691     Device::MediaTypes m  = d->doc->supportedMediaTypes() & d->doc->burner()->writeCapabilities();
0692     // if everything goes wrong we are left with no possible media to request
0693     if ( !m ) {
0694         emit infoMessage( i18n( "Internal Error: No medium type fits. This project cannot be burned." ), MessageError );
0695         return false;
0696     }
0697     const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject("cdrecord");
0698     emit newSubTask( i18n("Waiting for a medium") );
0699     Device::MediaType foundMedium = waitForMedium( d->doc->burner(),
0700                                                    usedMultiSessionMode() == K3b::DataDoc::CONTINUE ||
0701                                                    usedMultiSessionMode() == K3b::DataDoc::FINISH ?
0702                                                    K3b::Device::STATE_INCOMPLETE :
0703                                                    K3b::Device::STATE_EMPTY,
0704                                                    m,
0705                                                    d->doc->burningLength() );
0706 
0707     if( foundMedium == Device::MEDIA_UNKNOWN || hasBeenCanceled() ) {
0708         return false;
0709     }
0710 
0711     // -------------------------------
0712     // CD-R(W)
0713     // -------------------------------
0714     else if ( foundMedium & K3b::Device::MEDIA_CD_ALL ) {
0715         emit infoMessage( i18n( "Writing %1" , K3b::Device::mediaTypeString( foundMedium ) ), MessageInfo );
0716 
0717         // first of all we determine the data mode
0718         if( d->doc->dataMode() == K3b::DataModeAuto ) {
0719             if( !d->doc->onlyCreateImages() &&
0720                 ( usedMultiSessionMode() == K3b::DataDoc::CONTINUE ||
0721                   usedMultiSessionMode() == K3b::DataDoc::FINISH ) ) {
0722 
0723                 // try to get the last track's datamode
0724                 // we already asked for an appendable cdr when fetching
0725                 // the ms info
0726                 qDebug() << "(K3b::DataJob) determining last track's datamode...";
0727 
0728                 // FIXME: use the DeviceHandler
0729                 K3b::Device::Toc toc = d->doc->burner()->readToc();
0730                 if( toc.isEmpty() ) {
0731                     qDebug() << "(K3b::DataJob) could not retrieve toc.";
0732                     emit infoMessage( i18n("Unable to determine the last track's datamode. Using default."), MessageError );
0733                     d->usedDataMode = K3b::DataMode2;
0734                 }
0735                 else {
0736                     if( toc.back().mode() == K3b::Device::Track::MODE1 )
0737                         d->usedDataMode = K3b::DataMode1;
0738                     else
0739                         d->usedDataMode = K3b::DataMode2;
0740 
0741                     qDebug() << "(K3b::DataJob) using datamode: "
0742                              << (d->usedDataMode == K3b::DataMode1 ? "mode1" : "mode2")
0743                              << Qt::endl;
0744                 }
0745             }
0746             else if( usedMultiSessionMode() == K3b::DataDoc::NONE )
0747                 d->usedDataMode = K3b::DataMode1;
0748             else
0749                 d->usedDataMode = K3b::DataMode2;
0750         }
0751         else
0752             d->usedDataMode = d->doc->dataMode();
0753 
0754         // determine the writing mode
0755         if( d->doc->writingMode() == K3b::WritingModeAuto ) {
0756             // TODO: put this into the cdreocrdwriter and decide based on the size of the
0757             // track
0758             if( writer()->dao() && d->usedDataMode == K3b::DataMode1 &&
0759                 usedMultiSessionMode() == K3b::DataDoc::NONE )
0760                 d->usedWritingMode = K3b::WritingModeSao;
0761             else
0762                 d->usedWritingMode = K3b::WritingModeTao;
0763         }
0764         else
0765             d->usedWritingMode = d->doc->writingMode();
0766 
0767 
0768         if ( writingApp() == K3b::WritingAppGrowisofs ) {
0769             emit infoMessage( i18n( "Cannot write %1 media using %2. Falling back to default application." , QString("CD") , QString("growisofs") ), MessageWarning );
0770             setWritingApp( K3b::WritingAppAuto );
0771         }
0772         // cdrecord seems to have problems writing xa 1 disks in dao mode? At least on my system!
0773         if( writingApp() == K3b::WritingAppAuto ) {
0774             if( d->usedWritingMode == K3b::WritingModeSao ) {
0775                 if( usedMultiSessionMode() != K3b::DataDoc::NONE )
0776                     d->usedWritingApp = K3b::WritingAppCdrdao;
0777                 else if( d->usedDataMode == K3b::DataMode2 )
0778                     d->usedWritingApp = K3b::WritingAppCdrdao;
0779                 else
0780                     d->usedWritingApp = K3b::WritingAppCdrecord;
0781             }
0782             else
0783                 d->usedWritingApp = K3b::WritingAppCdrecord;
0784         }
0785         else {
0786             d->usedWritingApp = writingApp();
0787         }
0788     }
0789 
0790     else if ( foundMedium & K3b::Device::MEDIA_DVD_ALL ) {
0791         if ( writingApp() == K3b::WritingAppCdrdao ) {
0792             emit infoMessage( i18n( "Cannot write %1 media using %2. Falling back to default application.",
0793                                     K3b::Device::mediaTypeString( foundMedium, true ), "cdrdao" ), MessageWarning );
0794             setWritingApp( K3b::WritingAppAuto );
0795         }
0796 
0797         // make sure that we use the proper parameters for cdrecord
0798         d->usedDataMode = K3b::DataMode1;
0799 
0800         d->usedWritingApp = writingApp();
0801         // let's default to cdrecord for the time being (except for special cases below)
0802         // but prefer growisofs for DVDs
0803         if ( d->usedWritingApp == K3b::WritingAppAuto ) {
0804             if (cdrecordBin && cdrecordBin->hasFeature( "wodim" ))
0805                 d->usedWritingApp = K3b::WritingAppGrowisofs;
0806             else
0807                 d->usedWritingApp = K3b::WritingAppCdrecord;
0808         }
0809 
0810         // -------------------------------
0811         // DVD Plus
0812         // -------------------------------
0813         if( foundMedium & K3b::Device::MEDIA_DVD_PLUS_ALL ) {
0814             if( d->doc->dummy() ) {
0815                 if( !questionYesNo( i18n("%1 media do not support write simulation. "
0816                                          "Do you really want to continue? The disc will actually be "
0817                                          "written to.", Device::mediaTypeString(foundMedium, true)),
0818                                     i18n("No Simulation with %1", Device::mediaTypeString(foundMedium, true)) ) ) {
0819                     return false;
0820                 }
0821 
0822                 d->doc->setDummy( false );
0823             }
0824 
0825             if( d->doc->writingMode() != K3b::WritingModeAuto && d->doc->writingMode() != K3b::WritingModeRestrictedOverwrite )
0826                 emit infoMessage( i18n("Writing mode ignored when writing %1 media.", Device::mediaTypeString(foundMedium, true)), MessageInfo );
0827             d->usedWritingMode = K3b::WritingModeSao; // since cdrecord uses -sao for DVD+R(W)
0828 
0829             // Cdrecord doesn't support multisession DVD+R(W) disks
0830             if( usedMultiSessionMode() != DataDoc::NONE &&
0831                 d->usedWritingApp == K3b::WritingAppCdrecord ) {
0832                 d->usedWritingApp = WritingAppGrowisofs;
0833             }
0834 
0835             if( foundMedium & K3b::Device::MEDIA_DVD_PLUS_RW &&
0836                 ( usedMultiSessionMode() == K3b::DataDoc::CONTINUE ||
0837                   usedMultiSessionMode() == K3b::DataDoc::FINISH ) )
0838                 emit infoMessage( i18n("Growing ISO 9660 filesystem on %1.", Device::mediaTypeString(foundMedium, true)), MessageInfo );
0839             else
0840                 emit infoMessage( i18n("Writing %1.", Device::mediaTypeString(foundMedium, true)), MessageInfo );
0841         }
0842 
0843         // -------------------------------
0844         // DVD Minus
0845         // -------------------------------
0846         else if ( foundMedium & K3b::Device::MEDIA_DVD_MINUS_ALL ) {
0847             if( d->doc->dummy() && !d->doc->burner()->dvdMinusTestwrite() ) {
0848                 if( !questionYesNo( i18n("Your writer (%1 %2) does not support simulation with DVD-R(W) media. "
0849                                          "Do you really want to continue? The media will actually be "
0850                                          "written to.",
0851                                          d->doc->burner()->vendor(),
0852                                          d->doc->burner()->description()),
0853                                     i18n("No Simulation with DVD-R(W)") ) ) {
0854                     return false;
0855                 }
0856 
0857                 d->doc->setDummy( false );
0858             }
0859 
0860             // RESTRICTED OVERWRITE
0861             // --------------------
0862             if( foundMedium & K3b::Device::MEDIA_DVD_RW_OVWR ) {
0863                 d->usedWritingMode = K3b::WritingModeRestrictedOverwrite;
0864                 if( usedMultiSessionMode() == K3b::DataDoc::NONE ||
0865                     usedMultiSessionMode() == K3b::DataDoc::START ) {
0866                     // FIXME: can cdrecord handle this?
0867                     emit infoMessage( i18n("Writing DVD-RW in restricted overwrite mode."), MessageInfo );
0868                 }
0869                 else {
0870                     emit infoMessage( i18n("Growing ISO 9660 filesystem on DVD-RW in restricted overwrite mode."), MessageInfo );
0871                     // we can only do this with growisofs
0872                     d->usedWritingApp = K3b::WritingAppGrowisofs;
0873                 }
0874             }
0875 
0876             // NORMAL
0877             // ------
0878             else {
0879 
0880                 // FIXME: DVD-R DL jump and stuff
0881 
0882                 if( d->doc->writingMode() == K3b::WritingModeSao ) {
0883                     d->usedWritingMode = K3b::WritingModeSao;
0884                     emit infoMessage( i18n("Writing %1 in DAO mode.", K3b::Device::mediaTypeString(foundMedium, true) ), MessageInfo );
0885                 }
0886 
0887                 else {
0888                     // check if the writer supports writing sequential and thus multisession (on -1 the burner cannot handle
0889                     // features and we simply ignore it and hope for the best)
0890                     if( d->doc->burner()->featureCurrent( K3b::Device::FEATURE_INCREMENTAL_STREAMING_WRITABLE ) == 0 ) {
0891                         if( !questionYesNo( i18n("Your writer (%1 %2) does not support Incremental Streaming with %3 "
0892                                                  "media. Multisession will not be possible. Continue anyway?",
0893                                                  d->doc->burner()->vendor(),
0894                                                  d->doc->burner()->description(),
0895                                                  K3b::Device::mediaTypeString(foundMedium, true) ),
0896                                             i18n("No Incremental Streaming") ) ) {
0897                             return false;
0898                         }
0899                         else {
0900                             d->usedWritingMode = K3b::WritingModeSao;
0901                             emit infoMessage( i18n("Writing %1 in DAO mode.", K3b::Device::mediaTypeString(foundMedium, true) ), MessageInfo );
0902                         }
0903                     }
0904                     else {
0905                         d->usedWritingMode = K3b::WritingModeIncrementalSequential;
0906                         if( !(foundMedium & (K3b::Device::MEDIA_DVD_RW|K3b::Device::MEDIA_DVD_RW_OVWR|K3b::Device::MEDIA_DVD_RW_SEQ)) &&
0907                             d->doc->writingMode() == K3b::WritingModeRestrictedOverwrite )
0908                             emit infoMessage( i18n("Restricted Overwrite is not possible with DVD-R media."), MessageInfo );
0909 
0910                         emit infoMessage( i18n("Writing %1 in incremental mode.", K3b::Device::mediaTypeString(foundMedium, true) ), MessageInfo );
0911                     }
0912                 }
0913             }
0914         }
0915     }
0916 
0917     // --------------------
0918     // Blu-ray
0919     // --------------------
0920     else if ( foundMedium & K3b::Device::MEDIA_BD_ALL ) {
0921         d->usedWritingApp = writingApp();
0922         if( d->usedWritingApp == K3b::WritingAppAuto ) {
0923             if ( cdrecordBin && cdrecordBin->hasFeature( "wodim" ))
0924                 d->usedWritingApp = K3b::WritingAppGrowisofs;
0925             else
0926                 d->usedWritingApp = K3b::WritingAppCdrecord;
0927         }
0928 
0929         if (d->usedWritingApp == K3b::WritingAppCdrecord &&
0930             cdrecordBin && !cdrecordBin->hasFeature("blu-ray")) {
0931             d->usedWritingApp = K3b::WritingAppGrowisofs;
0932         }
0933 
0934         if( d->doc->dummy() ) {
0935             if( !questionYesNo( i18n("%1 media do not support write simulation. "
0936                                      "Do you really want to continue? The disc will actually be "
0937                                      "written to.", Device::mediaTypeString(foundMedium, true)),
0938                                 i18n("No Simulation with %1", Device::mediaTypeString(foundMedium, true)) ) ) {
0939                 return false;
0940             }
0941 
0942             d->doc->setDummy( false );
0943         }
0944 
0945         if( d->doc->writingMode() != K3b::WritingModeAuto )
0946             emit infoMessage( i18n("Writing mode ignored when writing %1 media.", Device::mediaTypeString(foundMedium, true)), MessageInfo );
0947         d->usedWritingMode = K3b::WritingModeSao; // cdrecord uses -sao for DVD+R(W), let's assume it's used also for BD-R(E)
0948 
0949         // Cdrecord probably doesn't support multisession BD-R disks
0950         // FIXME: check if above is actually true
0951         if( usedMultiSessionMode() != DataDoc::NONE &&
0952             d->usedWritingApp == K3b::WritingAppCdrecord ) {
0953             d->usedWritingApp = WritingAppGrowisofs;
0954         }
0955 
0956         if( foundMedium & K3b::Device::MEDIA_BD_RE &&
0957             ( usedMultiSessionMode() == K3b::DataDoc::CONTINUE ||
0958               usedMultiSessionMode() == K3b::DataDoc::FINISH ) )
0959             emit infoMessage( i18n("Growing ISO 9660 filesystem on %1.", Device::mediaTypeString(foundMedium, true)), MessageInfo );
0960         else
0961             emit infoMessage( i18n("Writing %1.", Device::mediaTypeString(foundMedium, true)), MessageInfo );
0962     }
0963 
0964     return true;
0965 }
0966 
0967 
0968 QString K3b::DataJob::jobDescription() const
0969 {
0970     if( d->doc->onlyCreateImages() ) {
0971         return i18n("Creating Data Image File");
0972     }
0973     else if( d->doc->multiSessionMode() == K3b::DataDoc::NONE ||
0974              d->doc->multiSessionMode() == K3b::DataDoc::AUTO ) {
0975         return i18n("Writing Data Project")
0976             + ( d->doc->isoOptions().volumeID().isEmpty()
0977                 ? QString()
0978                 : QString( " (%1)" ).arg(d->doc->isoOptions().volumeID()) );
0979     }
0980     else {
0981         return i18n("Writing Multisession Project")
0982             + ( d->doc->isoOptions().volumeID().isEmpty()
0983                 ? QString()
0984                 : QString( " (%1)" ).arg(d->doc->isoOptions().volumeID()) );
0985     }
0986 }
0987 
0988 
0989 QString K3b::DataJob::jobDetails() const
0990 {
0991     if( d->doc->copies() > 1 &&
0992         !d->doc->dummy() &&
0993         !(d->doc->multiSessionMode() == K3b::DataDoc::CONTINUE ||
0994           d->doc->multiSessionMode() == K3b::DataDoc::FINISH) )
0995         return i18np("ISO 9660 Filesystem (Size: %2) – One copy",
0996                      "ISO 9660 Filesystem (Size: %2) – %1 copies",
0997                      d->doc->copies(),
0998                      KIO::convertSize( d->doc->size() ) );
0999     else
1000         return i18n( "ISO 9660 Filesystem (Size: %1)",
1001                      KIO::convertSize( d->doc->size() ) );
1002 }
1003 
1004 
1005 K3b::DataDoc::MultiSessionMode K3b::DataJob::usedMultiSessionMode() const
1006 {
1007     return d->multiSessionParameterJob->usedMultiSessionMode();
1008 }
1009 
1010 
1011 void K3b::DataJob::cleanup()
1012 {
1013     qDebug();
1014     if( !d->doc->onTheFly() && ( d->doc->removeImages() || d->canceled ) ) {
1015         if( QFile::exists( d->doc->tempDir() ) ) {
1016             d->imageFile.remove();
1017             emit infoMessage( i18n("Removed image file %1",d->doc->tempDir()), K3b::Job::MessageSuccess );
1018         }
1019     }
1020 
1021     if( d->tocFile ) {
1022         delete d->tocFile;
1023         d->tocFile = 0;
1024     }
1025 }
1026 
1027 
1028 bool K3b::DataJob::hasBeenCanceled() const
1029 {
1030     return d->canceled;
1031 }
1032 
1033 
1034 bool K3b::DataJob::setupCdrecordJob()
1035 {
1036     qDebug();
1037     K3b::CdrecordWriter* writer = new K3b::CdrecordWriter( d->doc->burner(), this, this );
1038 
1039     // cdrecord manpage says that "not all" writers are able to write
1040     // multisession disks in dao mode. That means there are writers that can.
1041 
1042     // Does it really make sence to write Data ms cds in DAO mode since writing the
1043     // first session of a cd-extra in DAO mode is no problem with my writer while
1044     // writing the second data session is only possible in TAO mode.
1045     if( d->usedWritingMode == K3b::WritingModeSao &&
1046         usedMultiSessionMode() != K3b::DataDoc::NONE )
1047         emit infoMessage( i18n("Most writers do not support writing "
1048                                "multisession CDs in DAO mode."), MessageInfo );
1049 
1050     writer->setWritingMode( d->usedWritingMode );
1051     writer->setSimulate( d->doc->dummy() );
1052     writer->setBurnSpeed( d->doc->speed() );
1053 
1054     // multisession
1055     writer->setMulti( usedMultiSessionMode() == K3b::DataDoc::START ||
1056                       usedMultiSessionMode() == K3b::DataDoc::CONTINUE );
1057 
1058     if( d->doc->onTheFly() &&
1059         ( usedMultiSessionMode() == K3b::DataDoc::CONTINUE ||
1060           usedMultiSessionMode() == K3b::DataDoc::FINISH ) )
1061         writer->addArgument("-waiti");
1062 
1063     if( d->usedDataMode == K3b::DataMode1 )
1064         writer->addArgument( "-data" );
1065     else {
1066         const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject("cdrecord");
1067         if( cdrecordBin && cdrecordBin->hasFeature( "xamix" ) )
1068             writer->addArgument( "-xa" );
1069         else
1070             writer->addArgument( "-xa1" );
1071     }
1072 
1073     writer->addArgument( QString("-tsize=%1s").arg(m_isoImager->size()) )->addArgument("-");
1074 
1075     setWriterJob( writer );
1076 
1077     return true;
1078 }
1079 
1080 
1081 bool K3b::DataJob::setupCdrdaoJob()
1082 {
1083     // create cdrdao job
1084     K3b::CdrdaoWriter* writer = new K3b::CdrdaoWriter( d->doc->burner(), this, this );
1085     writer->setCommand( K3b::CdrdaoWriter::WRITE );
1086     writer->setSimulate( d->doc->dummy() );
1087     writer->setBurnSpeed( d->doc->speed() );
1088     // multisession
1089     writer->setMulti( usedMultiSessionMode() == K3b::DataDoc::START ||
1090                       usedMultiSessionMode() == K3b::DataDoc::CONTINUE );
1091 
1092     // now write the tocfile
1093     if( d->tocFile ) delete d->tocFile;
1094     d->tocFile = new QTemporaryFile( "XXXXXX.toc" );
1095     d->tocFile->open();
1096 
1097     QTextStream s( d->tocFile );
1098     if( d->usedDataMode == K3b::DataMode1 ) {
1099         s << "CD_ROM" << "\n";
1100         s << "\n";
1101         s << "TRACK MODE1" << "\n";
1102     }
1103     else {
1104         s << "CD_ROM_XA" << "\n";
1105         s << "\n";
1106         s << "TRACK MODE2_FORM1" << "\n";
1107     }
1108 
1109     s << "DATAFILE \"-\" " << m_isoImager->size()*2048 << "\n";
1110 
1111     d->tocFile->close();
1112 
1113     writer->setTocFile( d->tocFile->fileName() );
1114 
1115     setWriterJob( writer );
1116 
1117     return true;
1118 }
1119 
1120 
1121 bool K3b::DataJob::setupGrowisofsJob()
1122 {
1123     K3b::GrowisofsWriter* writer = new K3b::GrowisofsWriter( d->doc->burner(), this, this );
1124 
1125     // these do only make sense with DVD-R(W)
1126     writer->setSimulate( d->doc->dummy() );
1127     writer->setBurnSpeed( d->doc->speed() );
1128 
1129     // Andy said incremental sequential is the default mode and it seems uses have more problems with DAO anyway
1130     // BUT: I also had a report that incremental sequential produced unreadable media!
1131     if( d->doc->writingMode() == K3b::WritingModeSao )
1132 //     || ( d->doc->writingMode() == K3b::WritingModeAuto &&
1133 //   usedMultiSessionMode() == K3b::DataDoc::NONE ) )
1134         writer->setWritingMode( K3b::WritingModeSao );
1135 
1136     writer->setMultiSession( usedMultiSessionMode() == K3b::DataDoc::CONTINUE ||
1137                              usedMultiSessionMode() == K3b::DataDoc::FINISH );
1138 
1139     writer->setCloseDvd( usedMultiSessionMode() == K3b::DataDoc::NONE ||
1140                          usedMultiSessionMode() == K3b::DataDoc::FINISH );
1141 
1142     writer->setImageToWrite( QString() );  // read from stdin
1143     writer->setTrackSize( m_isoImager->size() );
1144 
1145     if( usedMultiSessionMode() != K3b::DataDoc::NONE ) {
1146         //
1147         // growisofs wants a valid -C parameter for multisession, so we get it from the
1148         // K3b::MsInfoFetcher (see K3b::DataJob::prepareWriting)
1149         //
1150         writer->setMultiSessionInfo( m_isoImager->multiSessionInfo() );
1151     }
1152 
1153     setWriterJob( writer );
1154 
1155     return true;
1156 }
1157 
1158 #include "moc_k3bdatajob.cpp"