1 /*
2     SPDX-FileCopyrightText: 2011 Michal Malek <michalm@jabster.pl>
3     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 
9 #include "k3bdatadoc.h"
10 #include "k3bfileitem.h"
11 #include "k3bdataitem.h"
12 #include "k3bdiritem.h"
13 #include "k3bsessionimportitem.h"
14 #include "k3bdatajob.h"
15 #include "k3bbootitem.h"
16 #include "k3bspecialdataitem.h"
17 #include "k3bfilecompilationsizehandler.h"
18 #include "k3bmkisofshandler.h"
19 #include "k3bcore.h"
20 #include "k3bglobals.h"
21 #include "k3bmsf.h"
22 #include "k3biso9660.h"
23 #include "k3bisooptions.h"
24 #include "k3bdevicehandler.h"
25 #include "k3bdevice.h"
26 #include "k3btoc.h"
27 #include "k3btrack.h"
28 #include "k3bmultichoicedialog.h"
29 #include "k3bvalidators.h"
30 #include "k3bglobalsettings.h"
31 #include "k3b_i18n.h"
32 
33 #include <KConfig>
34 #include <KMessageBox>
35 
36 #include <QDebug>
37 #include <QDir>
38 #include <QFile>
39 #include <QFileInfo>
40 #include <QStringList>
41 #include <QTimer>
42 #include <QApplication>
43 #include <QDomElement>
44 
45 #include <string.h>
46 #include <stdlib.h>
47 #include <ctype.h>
48 
49 
50 class K3b::DataDoc::Private
51 {
52 public:
Private()53     Private()
54     :
55         oldSessionSize( 0 ),
56         root( 0 ),
57         dataMode( 0 ),
58         verifyData( false ),
59         importedSession( -1 ),
60         bootCataloge( 0 ),
61         bExistingItemsReplaceAll( false ),
62         bExistingItemsIgnoreAll( false ),
63         needToCutFilenames( false )
64     {
65         sizeHandler = new K3b::FileCompilationSizeHandler();
66     }
67 
~Private()68     ~Private()
69     {
70         delete root;
71         delete sizeHandler;
72         //  delete oldSessionSizeHandler;
73     }
74 
75     FileCompilationSizeHandler* sizeHandler;
76 
77     //  FileCompilationSizeHandler* oldSessionSizeHandler;
78     KIO::filesize_t oldSessionSize;
79 
80     QStringList notFoundFiles;
81     QStringList noPermissionFiles;
82 
83     RootItem* root;
84 
85     int dataMode;
86 
87     bool verifyData;
88 
89     IsoOptions isoOptions;
90 
91     MultiSessionMode multisessionMode;
92     QList<DataItem*> oldSession;
93     int importedSession;
94 
95     // boot cd stuff
96     DataItem* bootCataloge;
97     QList<BootItem*> bootImages;
98 
99     bool bExistingItemsReplaceAll;
100     bool bExistingItemsIgnoreAll;
101 
102     bool needToCutFilenames;
103     QList<DataItem*> needToCutFilenameItems;
104 };
105 
106 
107 /**
108  * There are two ways to fill a data project with files and folders:
109  * \li Use the addUrl and addUrlsT methods
110  * \li or create your own K3b::DirItems and K3b::FileItems. The doc will be properly updated
111  *     by the constructors of the items.
112  */
DataDoc(QObject * parent)113 K3b::DataDoc::DataDoc( QObject* parent )
114     : K3b::Doc( parent ),
115       d( new Private )
116 {
117 }
118 
119 
~DataDoc()120 K3b::DataDoc::~DataDoc()
121 {
122     delete d;
123 }
124 
125 
newDocument()126 bool K3b::DataDoc::newDocument()
127 {
128     clear();
129     if ( !d->root )
130         d->root = new K3b::RootItem( *this );
131 
132     d->bExistingItemsReplaceAll = d->bExistingItemsIgnoreAll = false;
133 
134     d->multisessionMode = AUTO;
135     d->dataMode = K3b::DataModeAuto;
136 
137     d->isoOptions = K3b::IsoOptions();
138 
139     return K3b::Doc::newDocument();
140 }
141 
142 
clear()143 void K3b::DataDoc::clear()
144 {
145     clearImportedSession();
146     d->importedSession = -1;
147     d->oldSessionSize = 0;
148     d->bootCataloge = 0;
149     if( d->root ) {
150         while( !d->root->children().isEmpty() )
151             removeItem( d->root->children().first() );
152     }
153     d->sizeHandler->clear();
154     emit importedSessionChanged( importedSession() );
155 }
156 
157 
name() const158 QString K3b::DataDoc::name() const
159 {
160     return d->isoOptions.volumeID();
161 }
162 
163 
isoOptions() const164 const K3b::IsoOptions& K3b::DataDoc::isoOptions() const
165 {
166     return d->isoOptions;
167 }
168 
169 
setIsoOptions(const K3b::IsoOptions & isoOptions)170 void K3b::DataDoc::setIsoOptions( const K3b::IsoOptions& isoOptions )
171 {
172     d->isoOptions = isoOptions;
173     emit changed();
174 }
175 
176 
setVolumeID(const QString & v)177 void K3b::DataDoc::setVolumeID( const QString& v )
178 {
179     d->isoOptions.setVolumeID( v );
180     emit changed();
181     emit volumeIdChanged();
182 }
183 
184 
addUrls(const QList<QUrl> & urls)185 void K3b::DataDoc::addUrls( const QList<QUrl>& urls )
186 {
187     addUrlsToDir( urls, root() );
188 }
189 
190 
addUrlsToDir(const QList<QUrl> & l,K3b::DirItem * dir)191 void K3b::DataDoc::addUrlsToDir( const QList<QUrl>& l, K3b::DirItem* dir )
192 {
193     if( !dir )
194         dir = root();
195 
196     QList<QUrl> urls = K3b::convertToLocalUrls(l);
197 
198     for( QList<QUrl>::ConstIterator it = urls.constBegin(); it != urls.constEnd(); ++it ) {
199         const QUrl& url = *it;
200         QFileInfo f( url.toLocalFile() );
201         QString k3bname = f.absoluteFilePath().section( '/', -1 );
202 
203         // filenames cannot end in backslashes (mkisofs problem. See comments in k3bisoimager.cpp (escapeGraftPoint()))
204         while( k3bname[k3bname.length()-1] == '\\' )
205             k3bname.truncate( k3bname.length()-1 );
206 
207         // backup dummy name
208         if( k3bname.isEmpty() )
209             k3bname = '1';
210 
211         K3b::DirItem* newDirItem = 0;
212 
213         // rename the new item if an item with that name already exists
214         int cnt = 0;
215         bool ok = false;
216         while( !ok ) {
217             ok = true;
218             QString name( k3bname );
219             if( cnt > 0 )
220                 name += QString("_%1").arg(cnt);
221             if( K3b::DataItem* oldItem = dir->find( name ) ) {
222                 if( f.isDir() && oldItem->isDir() ) {
223                     // ok, just reuse the dir
224                     newDirItem = static_cast<K3b::DirItem*>(oldItem);
225                 }
226                 // directories cannot replace files in an old session (I think)
227                 // and also directories can for sure never be replaced (only be reused as above)
228                 // so we always rename if the old item is a dir.
229                 else if( !oldItem->isFromOldSession() ||
230                          f.isDir() ||
231                          oldItem->isDir() ) {
232                     ++cnt;
233                     ok = false;
234                 }
235             }
236         }
237         if( cnt > 0 )
238             k3bname += QString("_%1").arg(cnt);
239 
240         // QFileInfo::exists and QFileInfo::isReadable return false for broken symlinks :(
241         if( f.isDir() && !f.isSymLink() ) {
242             if( !newDirItem ) {
243                 newDirItem = new K3b::DirItem( k3bname );
244                 newDirItem->setLocalPath( url.toLocalFile() ); // HACK: see k3bdiritem.h
245                 dir->addDataItem( newDirItem );
246             }
247 
248             // recursively add all the files in the directory
249             QStringList dlist = QDir( f.absoluteFilePath() ).entryList( QDir::AllEntries|QDir::System|QDir::Hidden|QDir::NoDotAndDotDot );
250             QList<QUrl> newUrls;
251             for( QStringList::ConstIterator it = dlist.constBegin(); it != dlist.constEnd(); ++it )
252                 newUrls.append( QUrl::fromLocalFile( f.absoluteFilePath() + '/' + *it ) );
253             addUrlsToDir( newUrls, newDirItem );
254         }
255         else if( f.isSymLink() || f.isFile() ) {
256             dir->addDataItem( new FileItem( url.toLocalFile(), *this, k3bname ) );
257         }
258     }
259 
260     emit changed();
261 
262     setModified( true );
263 }
264 
265 
nameAlreadyInDir(const QString & name,K3b::DirItem * dir)266 bool K3b::DataDoc::nameAlreadyInDir( const QString& name, K3b::DirItem* dir )
267 {
268     if( !dir )
269         return false;
270     else
271         return ( dir->find( name ) != 0 );
272 }
273 
274 
addEmptyDir(const QString & name,K3b::DirItem * parent)275 K3b::DirItem* K3b::DataDoc::addEmptyDir( const QString& name, K3b::DirItem* parent )
276 {
277     if( parent != 0 ) {
278         K3b::DirItem* item = new K3b::DirItem( name );
279         parent->addDataItem( item );
280 
281         setModified( true );
282 
283         return item;
284     } else {
285         return 0;
286     }
287 }
288 
289 
size() const290 KIO::filesize_t K3b::DataDoc::size() const
291 {
292     if( d->isoOptions.doNotCacheInodes() )
293         return root()->blocks().mode1Bytes() + d->oldSessionSize;
294     else
295         return d->sizeHandler->blocks( d->isoOptions.followSymbolicLinks() ||
296                                       !d->isoOptions.createRockRidge() ).mode1Bytes();
297 }
298 
299 
burningSize() const300 KIO::filesize_t K3b::DataDoc::burningSize() const
301 {
302     return size() - d->oldSessionSize; //d->oldSessionSizeHandler->size();
303 }
304 
305 
length() const306 K3b::Msf K3b::DataDoc::length() const
307 {
308     // 1 block consists of 2048 bytes real data
309     // and 1 block equals to 1 audio frame
310     // so this is the way to calculate:
311 
312     return K3b::Msf( size() / 2048 );
313 }
314 
315 
burningLength() const316 K3b::Msf K3b::DataDoc::burningLength() const
317 {
318     return K3b::Msf( burningSize() / 2048 );
319 }
320 
321 
loadDocumentData(QDomElement * rootElem)322 bool K3b::DataDoc::loadDocumentData( QDomElement* rootElem )
323 {
324     if( !root() )
325         newDocument();
326 
327     QDomNodeList nodes = rootElem->childNodes();
328 
329     if( nodes.item(0).nodeName() != "general" ) {
330         qDebug() << "(K3b::DataDoc) could not find 'general' section.";
331         return false;
332     }
333     if( !readGeneralDocumentData( nodes.item(0).toElement() ) )
334         return false;
335 
336 
337     // parse options
338     // -----------------------------------------------------------------
339     if( nodes.item(1).nodeName() != "options" ) {
340         qDebug() << "(K3b::DataDoc) could not find 'options' section.";
341         return false;
342     }
343     if( !loadDocumentDataOptions( nodes.item(1).toElement() ) )
344         return false;
345     // -----------------------------------------------------------------
346 
347 
348 
349     // parse header
350     // -----------------------------------------------------------------
351     if( nodes.item(2).nodeName() != "header" ) {
352         qDebug() << "(K3b::DataDoc) could not find 'header' section.";
353         return false;
354     }
355     if( !loadDocumentDataHeader( nodes.item(2).toElement() ) )
356         return false;
357     // -----------------------------------------------------------------
358 
359 
360 
361     // parse files
362     // -----------------------------------------------------------------
363     if( nodes.item(3).nodeName() != "files" ) {
364         qDebug() << "(K3b::DataDoc) could not find 'files' section.";
365         return false;
366     }
367 
368     if( d->root == 0 )
369         d->root = new K3b::RootItem( *this );
370 
371     QDomNodeList filesList = nodes.item(3).childNodes();
372     for( int i = 0; i < filesList.count(); i++ ) {
373 
374         QDomElement e = filesList.item(i).toElement();
375         if( !loadDataItem( e, root() ) )
376             return false;
377     }
378 
379     // -----------------------------------------------------------------
380 
381     //
382     // Old versions of K3b do not properly save the boot catalog location
383     // and name. So to ensure we have one around even if loading an old project
384     // file we create a default one here.
385     //
386     if( !d->bootImages.isEmpty() && !d->bootCataloge )
387         createBootCatalogeItem( d->bootImages.first()->parent() );
388 
389 
390     informAboutNotFoundFiles();
391 
392     return true;
393 }
394 
395 
loadDocumentDataOptions(QDomElement elem)396 bool K3b::DataDoc::loadDocumentDataOptions( QDomElement elem )
397 {
398     QDomNodeList headerList = elem.childNodes();
399     for( int i = 0; i < headerList.count(); i++ ) {
400 
401         QDomElement e = headerList.item(i).toElement();
402         if( e.isNull() )
403             return false;
404 
405         if( e.nodeName() == "rock_ridge")
406             d->isoOptions.setCreateRockRidge( e.attributeNode( "activated" ).value() == "yes" );
407 
408         else if( e.nodeName() == "joliet")
409             d->isoOptions.setCreateJoliet( e.attributeNode( "activated" ).value() == "yes" );
410 
411         else if( e.nodeName() == "udf")
412             d->isoOptions.setCreateUdf( e.attributeNode( "activated" ).value() == "yes" );
413 
414         else if( e.nodeName() == "joliet_allow_103_characters")
415             d->isoOptions.setJolietLong( e.attributeNode( "activated" ).value() == "yes" );
416 
417         else if( e.nodeName() == "iso_allow_lowercase")
418             d->isoOptions.setISOallowLowercase( e.attributeNode( "activated" ).value() == "yes" );
419 
420         else if( e.nodeName() == "iso_allow_period_at_begin")
421             d->isoOptions.setISOallowPeriodAtBegin( e.attributeNode( "activated" ).value() == "yes" );
422 
423         else if( e.nodeName() == "iso_allow_31_char")
424             d->isoOptions.setISOallow31charFilenames( e.attributeNode( "activated" ).value() == "yes" );
425 
426         else if( e.nodeName() == "iso_omit_version_numbers")
427             d->isoOptions.setISOomitVersionNumbers( e.attributeNode( "activated" ).value() == "yes" );
428 
429         else if( e.nodeName() == "iso_omit_trailing_period")
430             d->isoOptions.setISOomitTrailingPeriod( e.attributeNode( "activated" ).value() == "yes" );
431 
432         else if( e.nodeName() == "iso_max_filename_length")
433             d->isoOptions.setISOmaxFilenameLength( e.attributeNode( "activated" ).value() == "yes" );
434 
435         else if( e.nodeName() == "iso_relaxed_filenames")
436             d->isoOptions.setISOrelaxedFilenames( e.attributeNode( "activated" ).value() == "yes" );
437 
438         else if( e.nodeName() == "iso_no_iso_translate")
439             d->isoOptions.setISOnoIsoTranslate( e.attributeNode( "activated" ).value() == "yes" );
440 
441         else if( e.nodeName() == "iso_allow_multidot")
442             d->isoOptions.setISOallowMultiDot( e.attributeNode( "activated" ).value() == "yes" );
443 
444         else if( e.nodeName() == "iso_untranslated_filenames")
445             d->isoOptions.setISOuntranslatedFilenames( e.attributeNode( "activated" ).value() == "yes" );
446 
447         else if( e.nodeName() == "follow_symbolic_links")
448             d->isoOptions.setFollowSymbolicLinks( e.attributeNode( "activated" ).value() == "yes" );
449 
450         else if( e.nodeName() == "create_trans_tbl")
451             d->isoOptions.setCreateTRANS_TBL( e.attributeNode( "activated" ).value() == "yes" );
452 
453         else if( e.nodeName() == "hide_trans_tbl")
454             d->isoOptions.setHideTRANS_TBL( e.attributeNode( "activated" ).value() == "yes" );
455 
456         else if( e.nodeName() == "iso_level")
457             d->isoOptions.setISOLevel( e.text().toInt() );
458 
459         else if( e.nodeName() == "discard_symlinks")
460             d->isoOptions.setDiscardSymlinks( e.attributeNode( "activated" ).value() == "yes" );
461 
462         else if( e.nodeName() == "discard_broken_symlinks")
463             d->isoOptions.setDiscardBrokenSymlinks( e.attributeNode( "activated" ).value() == "yes" );
464 
465         else if( e.nodeName() == "preserve_file_permissions")
466             d->isoOptions.setPreserveFilePermissions( e.attributeNode( "activated" ).value() == "yes" );
467 
468         else if( e.nodeName() == "do_not_cache_inodes" )
469             d->isoOptions.setDoNotCacheInodes( e.attributeNode( "activated" ).value() == "yes" );
470 
471         else if( e.nodeName() == "whitespace_treatment" ) {
472             if( e.text() == "strip" )
473                 d->isoOptions.setWhiteSpaceTreatment( K3b::IsoOptions::strip );
474             else if( e.text() == "extended" )
475                 d->isoOptions.setWhiteSpaceTreatment( K3b::IsoOptions::extended );
476             else if (e.text() == "replace")
477                 d->isoOptions.setWhiteSpaceTreatment( K3b::IsoOptions::replace );
478             else
479                 d->isoOptions.setWhiteSpaceTreatment( K3b::IsoOptions::noChange );
480         }
481 
482         else if( e.nodeName() == "whitespace_replace_string")
483             d->isoOptions.setWhiteSpaceTreatmentReplaceString( e.text() );
484 
485         else if( e.nodeName() == "data_track_mode" ) {
486             if( e.text() == "mode1" )
487                 d->dataMode = K3b::DataMode1;
488             else if( e.text() == "mode2" )
489                 d->dataMode = K3b::DataMode2;
490             else
491                 d->dataMode = K3b::DataModeAuto;
492         }
493 
494         else if( e.nodeName() == "multisession" ) {
495             QString mode = e.text();
496             if( mode == "start" )
497                 setMultiSessionMode( START );
498             else if( mode == "continue" )
499                 setMultiSessionMode( CONTINUE );
500             else if( mode == "finish" )
501                 setMultiSessionMode( FINISH );
502             else if( mode == "none" )
503                 setMultiSessionMode( NONE );
504             else
505                 setMultiSessionMode( AUTO );
506         }
507 
508         else if( e.nodeName() == "verify_data" )
509             setVerifyData( e.attributeNode( "activated" ).value() == "yes" );
510 
511         else
512             qDebug() << "(K3b::DataDoc) unknown option entry: " << e.nodeName();
513     }
514 
515     return true;
516 }
517 
518 
loadDocumentDataHeader(QDomElement headerElem)519 bool K3b::DataDoc::loadDocumentDataHeader( QDomElement headerElem )
520 {
521     QDomNodeList headerList = headerElem.childNodes();
522     for( int i = 0; i < headerList.count(); i++ ) {
523 
524         QDomElement e = headerList.item(i).toElement();
525         if( e.isNull() )
526             return false;
527 
528         if( e.nodeName() == "volume_id" )
529             d->isoOptions.setVolumeID( e.text() );
530 
531         else if( e.nodeName() == "application_id" )
532             d->isoOptions.setApplicationID( e.text() );
533 
534         else if( e.nodeName() == "publisher" )
535             d->isoOptions.setPublisher( e.text() );
536 
537         else if( e.nodeName() == "preparer" )
538             d->isoOptions.setPreparer( e.text() );
539 
540         else if( e.nodeName() == "volume_set_id" )
541             d->isoOptions.setVolumeSetId( e.text() );
542 
543         else if( e.nodeName() == "volume_set_size" )
544             d->isoOptions.setVolumeSetSize( e.text().toInt() );
545 
546         else if( e.nodeName() == "volume_set_number" )
547             d->isoOptions.setVolumeSetNumber( e.text().toInt() );
548 
549         else if( e.nodeName() == "system_id" )
550             d->isoOptions.setSystemId( e.text() );
551 
552         else
553             qDebug() << "(K3b::DataDoc) unknown header entry: " << e.nodeName();
554     }
555 
556     return true;
557 }
558 
559 
loadDataItem(QDomElement & elem,K3b::DirItem * parent)560 bool K3b::DataDoc::loadDataItem( QDomElement& elem, K3b::DirItem* parent )
561 {
562     if( !parent )
563         return false;
564 
565     K3b::DataItem* newItem = 0;
566 
567     if( elem.nodeName() == "file" ) {
568         QDomElement urlElem = elem.firstChild().toElement();
569         if( urlElem.isNull() ) {
570             qDebug() << "(K3b::DataDoc) file-element without url!";
571             return false;
572         }
573 
574         QFileInfo f( urlElem.text() );
575 
576         // We cannot use exists() here since this always disqualifies broken symlinks
577         if( !f.isFile() && !f.isSymLink() )
578             d->notFoundFiles.append( urlElem.text() );
579 
580         // broken symlinks are not readable according to QFileInfo which is wrong in our case
581         else if( f.isFile() && !f.isReadable() )
582             d->noPermissionFiles.append( urlElem.text() );
583 
584         else if( !elem.attribute( "bootimage" ).isEmpty() ) {
585             K3b::BootItem* bootItem = new K3b::BootItem( urlElem.text(),
586                                                          *this,
587                                                          elem.attributeNode( "name" ).value() );
588             parent->addDataItem( bootItem );
589             if( elem.attribute( "bootimage" ) == "floppy" )
590                 bootItem->setImageType( K3b::BootItem::FLOPPY );
591             else if( elem.attribute( "bootimage" ) == "harddisk" )
592                 bootItem->setImageType( K3b::BootItem::HARDDISK );
593             else
594                 bootItem->setImageType( K3b::BootItem::NONE );
595             bootItem->setNoBoot( elem.attribute( "no_boot" ) == "yes" );
596             bootItem->setBootInfoTable( elem.attribute( "boot_info_table" ) == "yes" );
597             bootItem->setLoadSegment( elem.attribute( "load_segment" ).toInt() );
598             bootItem->setLoadSize( elem.attribute( "load_size" ).toInt() );
599 
600             newItem = bootItem;
601         }
602 
603         else {
604             newItem = new K3b::FileItem( urlElem.text(),
605                                          *this,
606                                          elem.attributeNode( "name" ).value() );
607             parent->addDataItem( newItem );
608         }
609     }
610     else if( elem.nodeName() == "special" ) {
611         if( elem.attributeNode( "type" ).value() == "boot cataloge" )
612             createBootCatalogeItem( parent )->setK3bName( elem.attributeNode( "name" ).value() );
613     }
614     else if( elem.nodeName() == "directory" ) {
615         // This is for the VideoDVD project which already contains the *_TS folders
616         K3b::DirItem* newDirItem = 0;
617         if( K3b::DataItem* item = parent->find( elem.attributeNode( "name" ).value() ) ) {
618             if( item->isDir() ) {
619                 newDirItem = static_cast<K3b::DirItem*>(item);
620             }
621             else {
622                 qCritical() << "(K3b::DataDoc) INVALID DOCUMENT: item " << item->k3bPath() << " saved twice" << endl;
623                 return false;
624             }
625         }
626 
627         if( !newDirItem ) {
628             newDirItem = new K3b::DirItem( elem.attributeNode( "name" ).value() );
629             parent->addDataItem( newDirItem );
630         }
631         QDomNodeList childNodes = elem.childNodes();
632         for( int i = 0; i < childNodes.count(); i++ ) {
633 
634             QDomElement e = childNodes.item(i).toElement();
635             if( !loadDataItem( e, newDirItem ) )
636                 return false;
637         }
638 
639         newItem = newDirItem;
640     }
641     else {
642         qDebug() << "(K3b::DataDoc) wrong tag in files-section: " << elem.nodeName();
643         return false;
644     }
645 
646     // load the sort weight
647     if( newItem )
648         newItem->setSortWeight( elem.attribute( "sort_weight", "0" ).toInt() );
649 
650     return true;
651 }
652 
653 
saveDocumentData(QDomElement * docElem)654 bool K3b::DataDoc::saveDocumentData( QDomElement* docElem )
655 {
656     QDomDocument doc = docElem->ownerDocument();
657 
658     saveGeneralDocumentData( docElem );
659 
660     // all options
661     // ----------------------------------------------------------------------
662     QDomElement optionsElem = doc.createElement( "options" );
663     saveDocumentDataOptions( optionsElem );
664     docElem->appendChild( optionsElem );
665     // ----------------------------------------------------------------------
666 
667     // the header stuff
668     // ----------------------------------------------------------------------
669     QDomElement headerElem = doc.createElement( "header" );
670     saveDocumentDataHeader( headerElem );
671     docElem->appendChild( headerElem );
672 
673 
674     // now do the "real" work: save the entries
675     // ----------------------------------------------------------------------
676     QDomElement topElem = doc.createElement( "files" );
677 
678     Q_FOREACH( K3b::DataItem* item, root()->children() ) {
679         saveDataItem( item, &doc, &topElem );
680     }
681 
682     docElem->appendChild( topElem );
683     // ----------------------------------------------------------------------
684 
685     return true;
686 }
687 
688 
saveDocumentDataOptions(QDomElement & optionsElem)689 void K3b::DataDoc::saveDocumentDataOptions( QDomElement& optionsElem )
690 {
691     QDomDocument doc = optionsElem.ownerDocument();
692 
693     QDomElement topElem = doc.createElement( "rock_ridge" );
694     topElem.setAttribute( "activated", isoOptions().createRockRidge() ? "yes" : "no" );
695     optionsElem.appendChild( topElem );
696 
697     topElem = doc.createElement( "joliet" );
698     topElem.setAttribute( "activated", isoOptions().createJoliet() ? "yes" : "no" );
699     optionsElem.appendChild( topElem );
700 
701     topElem = doc.createElement( "udf" );
702     topElem.setAttribute( "activated", isoOptions().createUdf() ? "yes" : "no" );
703     optionsElem.appendChild( topElem );
704 
705     topElem = doc.createElement( "joliet_allow_103_characters" );
706     topElem.setAttribute( "activated", isoOptions().jolietLong() ? "yes" : "no" );
707     optionsElem.appendChild( topElem );
708 
709     topElem = doc.createElement( "iso_allow_lowercase" );
710     topElem.setAttribute( "activated", isoOptions().ISOallowLowercase() ? "yes" : "no" );
711     optionsElem.appendChild( topElem );
712 
713     topElem = doc.createElement( "iso_allow_period_at_begin" );
714     topElem.setAttribute( "activated", isoOptions().ISOallowPeriodAtBegin() ? "yes" : "no" );
715     optionsElem.appendChild( topElem );
716 
717     topElem = doc.createElement( "iso_allow_31_char" );
718     topElem.setAttribute( "activated", isoOptions().ISOallow31charFilenames() ? "yes" : "no" );
719     optionsElem.appendChild( topElem );
720 
721     topElem = doc.createElement( "iso_omit_version_numbers" );
722     topElem.setAttribute( "activated", isoOptions().ISOomitVersionNumbers() ? "yes" : "no" );
723     optionsElem.appendChild( topElem );
724 
725     topElem = doc.createElement( "iso_omit_trailing_period" );
726     topElem.setAttribute( "activated", isoOptions().ISOomitTrailingPeriod() ? "yes" : "no" );
727     optionsElem.appendChild( topElem );
728 
729     topElem = doc.createElement( "iso_max_filename_length" );
730     topElem.setAttribute( "activated", isoOptions().ISOmaxFilenameLength() ? "yes" : "no" );
731     optionsElem.appendChild( topElem );
732 
733     topElem = doc.createElement( "iso_relaxed_filenames" );
734     topElem.setAttribute( "activated", isoOptions().ISOrelaxedFilenames() ? "yes" : "no" );
735     optionsElem.appendChild( topElem );
736 
737     topElem = doc.createElement( "iso_no_iso_translate" );
738     topElem.setAttribute( "activated", isoOptions().ISOnoIsoTranslate() ? "yes" : "no" );
739     optionsElem.appendChild( topElem );
740 
741     topElem = doc.createElement( "iso_allow_multidot" );
742     topElem.setAttribute( "activated", isoOptions().ISOallowMultiDot() ? "yes" : "no" );
743     optionsElem.appendChild( topElem );
744 
745     topElem = doc.createElement( "iso_untranslated_filenames" );
746     topElem.setAttribute( "activated", isoOptions().ISOuntranslatedFilenames() ? "yes" : "no" );
747     optionsElem.appendChild( topElem );
748 
749     topElem = doc.createElement( "follow_symbolic_links" );
750     topElem.setAttribute( "activated", isoOptions().followSymbolicLinks() ? "yes" : "no" );
751     optionsElem.appendChild( topElem );
752 
753     topElem = doc.createElement( "create_trans_tbl" );
754     topElem.setAttribute( "activated", isoOptions().createTRANS_TBL() ? "yes" : "no" );
755     optionsElem.appendChild( topElem );
756 
757     topElem = doc.createElement( "hide_trans_tbl" );
758     topElem.setAttribute( "activated", isoOptions().hideTRANS_TBL() ? "yes" : "no" );
759     optionsElem.appendChild( topElem );
760 
761     topElem = doc.createElement( "iso_level" );
762     topElem.appendChild( doc.createTextNode( QString::number(isoOptions().ISOLevel()) ) );
763     optionsElem.appendChild( topElem );
764 
765     topElem = doc.createElement( "discard_symlinks" );
766     topElem.setAttribute( "activated", isoOptions().discardSymlinks() ? "yes" : "no" );
767     optionsElem.appendChild( topElem );
768 
769     topElem = doc.createElement( "discard_broken_symlinks" );
770     topElem.setAttribute( "activated", isoOptions().discardBrokenSymlinks() ? "yes" : "no" );
771     optionsElem.appendChild( topElem );
772 
773     topElem = doc.createElement( "preserve_file_permissions" );
774     topElem.setAttribute( "activated", isoOptions().preserveFilePermissions() ? "yes" : "no" );
775     optionsElem.appendChild( topElem );
776 
777     topElem = doc.createElement( "do_not_cache_inodes" );
778     topElem.setAttribute( "activated", isoOptions().doNotCacheInodes() ? "yes" : "no" );
779     optionsElem.appendChild( topElem );
780 
781 
782     topElem = doc.createElement( "whitespace_treatment" );
783     switch( isoOptions().whiteSpaceTreatment() ) {
784     case K3b::IsoOptions::strip:
785         topElem.appendChild( doc.createTextNode( "strip" ) );
786         break;
787     case K3b::IsoOptions::extended:
788         topElem.appendChild( doc.createTextNode( "extended" ) );
789         break;
790     case K3b::IsoOptions::replace:
791         topElem.appendChild( doc.createTextNode( "replace" ) );
792         break;
793     default:
794         topElem.appendChild( doc.createTextNode( "noChange" ) );
795         break;
796     }
797     optionsElem.appendChild( topElem );
798 
799     topElem = doc.createElement( "whitespace_replace_string" );
800     topElem.appendChild( doc.createTextNode( isoOptions().whiteSpaceTreatmentReplaceString() ) );
801     optionsElem.appendChild( topElem );
802 
803     topElem = doc.createElement( "data_track_mode" );
804     if( d->dataMode == K3b::DataMode1 )
805         topElem.appendChild( doc.createTextNode( "mode1" ) );
806     else if( d->dataMode == K3b::DataMode2 )
807         topElem.appendChild( doc.createTextNode( "mode2" ) );
808     else
809         topElem.appendChild( doc.createTextNode( "auto" ) );
810     optionsElem.appendChild( topElem );
811 
812 
813     // save multisession
814     topElem = doc.createElement( "multisession" );
815     switch( d->multisessionMode ) {
816     case START:
817         topElem.appendChild( doc.createTextNode( "start" ) );
818         break;
819     case CONTINUE:
820         topElem.appendChild( doc.createTextNode( "continue" ) );
821         break;
822     case FINISH:
823         topElem.appendChild( doc.createTextNode( "finish" ) );
824         break;
825     case NONE:
826         topElem.appendChild( doc.createTextNode( "none" ) );
827         break;
828     default:
829         topElem.appendChild( doc.createTextNode( "auto" ) );
830         break;
831     }
832     optionsElem.appendChild( topElem );
833 
834     topElem = doc.createElement( "verify_data" );
835     topElem.setAttribute( "activated", verifyData() ? "yes" : "no" );
836     optionsElem.appendChild( topElem );
837     // ----------------------------------------------------------------------
838 }
839 
840 
saveDocumentDataHeader(QDomElement & headerElem)841 void K3b::DataDoc::saveDocumentDataHeader( QDomElement& headerElem )
842 {
843     QDomDocument doc = headerElem.ownerDocument();
844 
845     QDomElement topElem = doc.createElement( "volume_id" );
846     topElem.appendChild( doc.createTextNode( isoOptions().volumeID() ) );
847     headerElem.appendChild( topElem );
848 
849     topElem = doc.createElement( "volume_set_id" );
850     topElem.appendChild( doc.createTextNode( isoOptions().volumeSetId() ) );
851     headerElem.appendChild( topElem );
852 
853     topElem = doc.createElement( "volume_set_size" );
854     topElem.appendChild( doc.createTextNode( QString::number(isoOptions().volumeSetSize()) ) );
855     headerElem.appendChild( topElem );
856 
857     topElem = doc.createElement( "volume_set_number" );
858     topElem.appendChild( doc.createTextNode( QString::number(isoOptions().volumeSetNumber()) ) );
859     headerElem.appendChild( topElem );
860 
861     topElem = doc.createElement( "system_id" );
862     topElem.appendChild( doc.createTextNode( isoOptions().systemId() ) );
863     headerElem.appendChild( topElem );
864 
865     topElem = doc.createElement( "application_id" );
866     topElem.appendChild( doc.createTextNode( isoOptions().applicationID() ) );
867     headerElem.appendChild( topElem );
868 
869     topElem = doc.createElement( "publisher" );
870     topElem.appendChild( doc.createTextNode( isoOptions().publisher() ) );
871     headerElem.appendChild( topElem );
872 
873     topElem = doc.createElement( "preparer" );
874     topElem.appendChild( doc.createTextNode( isoOptions().preparer() ) );
875     headerElem.appendChild( topElem );
876     // ----------------------------------------------------------------------
877 }
878 
879 
saveDataItem(K3b::DataItem * item,QDomDocument * doc,QDomElement * parent)880 void K3b::DataDoc::saveDataItem( K3b::DataItem* item, QDomDocument* doc, QDomElement* parent )
881 {
882     if( K3b::FileItem* fileItem = dynamic_cast<K3b::FileItem*>( item ) ) {
883         if( d->oldSession.contains( fileItem ) ) {
884             qDebug() << "(K3b::DataDoc) ignoring fileitem " << fileItem->k3bName() << " from old session while saving...";
885         }
886         else {
887             QDomElement topElem = doc->createElement( "file" );
888             topElem.setAttribute( "name", fileItem->k3bName() );
889             QDomElement subElem = doc->createElement( "url" );
890             subElem.appendChild( doc->createTextNode( fileItem->localPath() ) );
891             topElem.appendChild( subElem );
892 
893             if( item->sortWeight() != 0 )
894                 topElem.setAttribute( "sort_weight", QString::number(item->sortWeight()) );
895 
896             parent->appendChild( topElem );
897 
898             // add boot options as attributes to preserve compatibility to older K3b versions
899             if( K3b::BootItem* bootItem = dynamic_cast<K3b::BootItem*>( fileItem ) ) {
900                 if( bootItem->imageType() == K3b::BootItem::FLOPPY )
901                     topElem.setAttribute( "bootimage", "floppy" );
902                 else if( bootItem->imageType() == K3b::BootItem::HARDDISK )
903                     topElem.setAttribute( "bootimage", "harddisk" );
904                 else
905                     topElem.setAttribute( "bootimage", "none" );
906 
907                 topElem.setAttribute( "no_boot", bootItem->noBoot() ? "yes" : "no" );
908                 topElem.setAttribute( "boot_info_table", bootItem->bootInfoTable() ? "yes" : "no" );
909                 topElem.setAttribute( "load_segment", QString::number( bootItem->loadSegment() ) );
910                 topElem.setAttribute( "load_size", QString::number( bootItem->loadSize() ) );
911             }
912         }
913     }
914     else if( item == d->bootCataloge ) {
915         QDomElement topElem = doc->createElement( "special" );
916         topElem.setAttribute( "name", d->bootCataloge->k3bName() );
917         topElem.setAttribute( "type", "boot cataloge" );
918 
919         parent->appendChild( topElem );
920     }
921     else if( K3b::DirItem* dirItem = dynamic_cast<K3b::DirItem*>( item ) ) {
922         QDomElement topElem = doc->createElement( "directory" );
923         topElem.setAttribute( "name", dirItem->k3bName() );
924 
925         if( item->sortWeight() != 0 )
926             topElem.setAttribute( "sort_weight", QString::number(item->sortWeight()) );
927 
928         Q_FOREACH( K3b::DataItem* item, dirItem->children() ) {
929             saveDataItem( item, doc, &topElem );
930         }
931 
932         parent->appendChild( topElem );
933     }
934 }
935 
936 
removeItem(K3b::DataItem * item)937 void K3b::DataDoc::removeItem( K3b::DataItem* item )
938 {
939     if( !item )
940         return;
941 
942     if( item->isRemoveable() ) {
943         delete item;
944     }
945     else
946         qDebug() << "(K3b::DataDoc) tried to remove non-removable entry!";
947 }
948 
949 
removeItems(K3b::DirItem * parent,int start,int count)950 void K3b::DataDoc::removeItems( K3b::DirItem* parent, int start, int count )
951 {
952     if( parent ) {
953         parent->removeDataItems( start, count );
954     }
955 }
956 
957 
beginInsertItems(DirItem * parent,int start,int end)958 void K3b::DataDoc::beginInsertItems( DirItem* parent, int start, int end )
959 {
960     emit itemsAboutToBeInserted( parent, start, end );
961 }
962 
963 
endInsertItems(DirItem * parent,int start,int end)964 void K3b::DataDoc::endInsertItems( DirItem* parent, int start, int end )
965 {
966     for( int i = start; i <= end; ++i ) {
967         DataItem* item = parent->children().at( i );
968         // update the project size
969         if( !item->isFromOldSession() )
970             d->sizeHandler->addFile( item );
971 
972         // update the boot item list
973         if( item->isBootItem() )
974             d->bootImages.append( static_cast<K3b::BootItem*>( item ) );
975     }
976 
977     emit itemsInserted( parent, start, end );
978     emit changed();
979 }
980 
981 
beginRemoveItems(DirItem * parent,int start,int end)982 void K3b::DataDoc::beginRemoveItems( DirItem* parent, int start, int end )
983 {
984     emit itemsAboutToBeRemoved( parent, start, end );
985 
986     for( int i = start; i <= end; ++i ) {
987         DataItem* item = parent->children().at( i );
988         // update the project size
989         if( !item->isFromOldSession() )
990             d->sizeHandler->removeFile( item );
991 
992         // update the boot item list
993         if( item->isBootItem() ) {
994             d->bootImages.removeAll( static_cast<K3b::BootItem*>( item ) );
995             if( d->bootImages.isEmpty() ) {
996                 delete d->bootCataloge;
997                 d->bootCataloge = 0;
998             }
999         }
1000     }
1001 }
1002 
1003 
endRemoveItems(DirItem * parent,int start,int end)1004 void K3b::DataDoc::endRemoveItems( DirItem* parent, int start, int end )
1005 {
1006     emit itemsRemoved( parent, start, end );
1007     emit changed();
1008 }
1009 
1010 
moveItem(K3b::DataItem * item,K3b::DirItem * newParent)1011 void K3b::DataDoc::moveItem( K3b::DataItem* item, K3b::DirItem* newParent )
1012 {
1013     if( !item || !newParent ) {
1014         qDebug() << "(K3b::DataDoc) item or parentitem was NULL while moving.";
1015         return;
1016     }
1017 
1018     if( !item->isMoveable() ) {
1019         qDebug() << "(K3b::DataDoc) item is not movable! ";
1020         return;
1021     }
1022 
1023     item->reparent( newParent );
1024 }
1025 
1026 
moveItems(const QList<K3b::DataItem * > & itemList,K3b::DirItem * newParent)1027 void K3b::DataDoc::moveItems( const QList<K3b::DataItem*>& itemList, K3b::DirItem* newParent )
1028 {
1029     if( !newParent ) {
1030         qDebug() << "(K3b::DataDoc) tried to move items to nowhere...!";
1031         return;
1032     }
1033 
1034     Q_FOREACH( K3b::DataItem* item, itemList ) {
1035         // check if newParent is subdir of item
1036         if( K3b::DirItem* dirItem = dynamic_cast<K3b::DirItem*>( item ) ) {
1037             if( dirItem->isSubItem( newParent ) ) {
1038                 continue;
1039             }
1040         }
1041 
1042         if( item->isMoveable() )
1043             item->reparent( newParent );
1044     }
1045 }
1046 
1047 
newBurnJob(K3b::JobHandler * hdl,QObject * parent)1048 K3b::BurnJob* K3b::DataDoc::newBurnJob( K3b::JobHandler* hdl, QObject* parent )
1049 {
1050     return new K3b::DataJob( this, hdl, parent );
1051 }
1052 
1053 
treatWhitespace(const QString & path)1054 QString K3b::DataDoc::treatWhitespace( const QString& path )
1055 {
1056 
1057     // TODO:
1058     // It could happen that two files with different names
1059     // will have the same name after the treatment
1060     // Perhaps we should add a number at the end or something
1061     // similar (s.a.)
1062 
1063 
1064     if( isoOptions().whiteSpaceTreatment() != K3b::IsoOptions::noChange ) {
1065         QString result = path;
1066 
1067         if( isoOptions().whiteSpaceTreatment() == K3b::IsoOptions::replace ) {
1068             result.replace( ' ', isoOptions().whiteSpaceTreatmentReplaceString() );
1069         }
1070         else if( isoOptions().whiteSpaceTreatment() == K3b::IsoOptions::strip ) {
1071             result.remove( ' ' );
1072         }
1073         else if( isoOptions().whiteSpaceTreatment() == K3b::IsoOptions::extended ) {
1074             result.truncate(0);
1075             for( int i = 0; i < path.length(); i++ ) {
1076                 if( path[i] == ' ' ) {
1077                     if( path[i+1] != ' ' )
1078                         result.append( path[++i].toUpper() );
1079                 }
1080                 else
1081                     result.append( path[i] );
1082             }
1083         }
1084 
1085         qDebug() << "(K3b::DataDoc) converted " << path << " to " << result;
1086         return result;
1087     }
1088     else
1089         return path;
1090 }
1091 
1092 
prepareFilenames()1093 void K3b::DataDoc::prepareFilenames()
1094 {
1095     d->needToCutFilenames = false;
1096     d->needToCutFilenameItems.clear();
1097 
1098     //
1099     // if joliet is used cut the names and rename if necessary
1100     // 64 characters for standard joliet and 103 characters for long joliet names
1101     //
1102     // Rockridge supports the full 255 UNIX chars and in case Rockridge is disabled we leave
1103     // it to mkisofs for now since handling all the options to alter the ISO9660 standard it just
1104     // too much.
1105     //
1106 
1107     K3b::DataItem* item = root();
1108     int maxlen = ( isoOptions().jolietLong() ? 103 : 64 );
1109     while( (item = item->nextSibling()) ) {
1110         item->setWrittenName( treatWhitespace( item->k3bName() ) );
1111 
1112         if( isoOptions().createJoliet() && item->writtenName().length() > maxlen ) {
1113             d->needToCutFilenames = true;
1114             item->setWrittenName( K3b::cutFilename( item->writtenName(), maxlen ) );
1115             d->needToCutFilenameItems.append( item );
1116         }
1117 
1118         // TODO: check the Joliet charset
1119     }
1120 
1121     //
1122     // 3. check if a directory contains items with the same name
1123     //
1124     prepareFilenamesInDir( root() );
1125 }
1126 
1127 
prepareFilenamesInDir(K3b::DirItem * dir)1128 void K3b::DataDoc::prepareFilenamesInDir( K3b::DirItem* dir )
1129 {
1130     if( !dir )
1131         return;
1132 
1133     QList<K3b::DataItem*> sortedChildren;
1134     QList<K3b::DataItem*> children( dir->children() );
1135     QList<K3b::DataItem*>::const_iterator it = children.constEnd();
1136     while ( it != children.constBegin() ) {
1137         --it;
1138         K3b::DataItem* item = *it;
1139 
1140         if( item->isDir() )
1141             prepareFilenamesInDir( dynamic_cast<K3b::DirItem*>( item ) );
1142 
1143         // insertion sort
1144         int i = 0;
1145         while( i < sortedChildren.count() && item->writtenName() > sortedChildren.at(i)->writtenName() )
1146             ++i;
1147 
1148         sortedChildren.insert( i, item );
1149     }
1150 
1151     if( isoOptions().createJoliet() || isoOptions().createRockRidge() ) {
1152         QList<K3b::DataItem*> sameNameList;
1153         while( !sortedChildren.isEmpty() ) {
1154 
1155             sameNameList.clear();
1156 
1157             do {
1158                 sameNameList.append( sortedChildren.takeFirst() );
1159             } while( !sortedChildren.isEmpty() &&
1160                      sortedChildren.first()->writtenName() == sameNameList.first()->writtenName() );
1161 
1162             if( sameNameList.count() > 1 ) {
1163                 // now we need to rename the items
1164                 unsigned int maxlen = 255;
1165                 if( isoOptions().createJoliet() ) {
1166                     if( isoOptions().jolietLong() )
1167                         maxlen = 103;
1168                     else
1169                         maxlen = 64;
1170                 }
1171 
1172                 int cnt = 1;
1173                 Q_FOREACH( K3b::DataItem* item, sameNameList ) {
1174                     item->setWrittenName( K3b::appendNumberToFilename( item->writtenName(), cnt++, maxlen ) );
1175                 }
1176             }
1177         }
1178     }
1179 }
1180 
1181 
needToCutFilenames() const1182 bool K3b::DataDoc::needToCutFilenames() const
1183 {
1184     return d->needToCutFilenames;
1185 }
1186 
1187 
needToCutFilenameItems() const1188 QList<K3b::DataItem*> K3b::DataDoc::needToCutFilenameItems() const
1189 {
1190     return d->needToCutFilenameItems;
1191 }
1192 
1193 
informAboutNotFoundFiles()1194 void K3b::DataDoc::informAboutNotFoundFiles()
1195 {
1196     if( !d->notFoundFiles.isEmpty() ) {
1197         KMessageBox::informationList( qApp->activeWindow(), i18n("Could not find the following files:"),
1198                                       d->notFoundFiles, i18n("Not Found") );
1199         d->notFoundFiles.clear();
1200     }
1201 
1202     if( !d->noPermissionFiles.isEmpty() ) {
1203         KMessageBox::informationList( qApp->activeWindow(), i18n("No permission to read the following files:"),
1204                                       d->noPermissionFiles, i18n("No Read Permission") );
1205 
1206         d->noPermissionFiles.clear();
1207     }
1208 }
1209 
1210 
multiSessionMode() const1211 K3b::DataDoc::MultiSessionMode K3b::DataDoc::multiSessionMode() const
1212 {
1213     return d->multisessionMode;
1214 }
1215 
1216 
setMultiSessionMode(K3b::DataDoc::MultiSessionMode mode)1217 void K3b::DataDoc::setMultiSessionMode( K3b::DataDoc::MultiSessionMode mode )
1218 {
1219     if( d->multisessionMode == NONE || d->multisessionMode == START )
1220         clearImportedSession();
1221 
1222     d->multisessionMode = mode;
1223 }
1224 
1225 
dataMode() const1226 int K3b::DataDoc::dataMode() const
1227 {
1228     return d->dataMode;
1229 }
1230 
1231 
setDataMode(int m)1232 void K3b::DataDoc::setDataMode( int m )
1233 {
1234     d->dataMode = m;
1235 }
1236 
1237 
setVerifyData(bool b)1238 void K3b::DataDoc::setVerifyData( bool b )
1239 {
1240     d->verifyData = b;
1241 }
1242 
1243 
verifyData() const1244 bool K3b::DataDoc::verifyData() const
1245 {
1246     return d->verifyData;
1247 }
1248 
1249 
importSession(K3b::Device::Device * device,int session)1250 bool K3b::DataDoc::importSession( K3b::Device::Device* device, int session )
1251 {
1252     K3b::Device::DiskInfo diskInfo = device->diskInfo();
1253     // DVD+RW media is reported as non-appendable
1254     if( !diskInfo.appendable() &&
1255         !(diskInfo.mediaType() & (K3b::Device::MEDIA_DVD_PLUS_RW|K3b::Device::MEDIA_DVD_RW_OVWR)) )
1256         return false;
1257 
1258     K3b::Device::Toc toc = device->readToc();
1259     if( toc.isEmpty() ||
1260         toc.last().type() != K3b::Device::Track::TYPE_DATA )
1261         return false;
1262 
1263     long startSec = toc.last().firstSector().lba();
1264     if ( session > 0 ) {
1265         for ( K3b::Device::Toc::const_iterator it = toc.constBegin(); it != toc.constEnd(); ++it ) {
1266             if ( ( *it ).session() == session ) {
1267                 startSec = ( *it ).firstSector().lba();
1268                 break;
1269             }
1270         }
1271     }
1272     K3b::Iso9660 iso( device, startSec );
1273 
1274     if( iso.open() ) {
1275         // remove previously imported sessions
1276         clearImportedSession();
1277 
1278         // set multisession option
1279         if( d->multisessionMode != FINISH && d->multisessionMode != AUTO )
1280             d->multisessionMode = CONTINUE;
1281 
1282         // since in iso9660 it is possible that two files share it's data
1283         // simply summing the file sizes could result in wrong values
1284         // that's why we use the size from the toc. This is more accurate
1285         // anyway since there might be files overwritten or removed
1286         d->oldSessionSize = toc.last().lastSector().mode1Bytes();
1287         d->importedSession = session;
1288 
1289         qDebug() << "(K3b::DataDoc) imported session size: " << KIO::convertSize(d->oldSessionSize);
1290 
1291         // the track size for DVD+RW media and DVD-RW Overwrite media has nothing to do with the filesystem
1292         // size. in that case we need to use the filesystem's size (which is ok since it's one track anyway,
1293         // no real multisession)
1294         if( diskInfo.mediaType() & (K3b::Device::MEDIA_DVD_PLUS_RW|K3b::Device::MEDIA_DVD_RW_OVWR) ) {
1295             d->oldSessionSize = iso.primaryDescriptor().volumeSpaceSize
1296                                * iso.primaryDescriptor().logicalBlockSize;
1297         }
1298 
1299         // import some former settings
1300         d->isoOptions.setCreateRockRidge( iso.firstRRDirEntry() != 0 );
1301         d->isoOptions.setCreateJoliet( iso.firstJolietDirEntry() != 0 );
1302         d->isoOptions.setVolumeID( iso.primaryDescriptor().volumeId );
1303         // TODO: also import some other pd fields
1304 
1305         const K3b::Iso9660Directory* rootDir = iso.firstRRDirEntry();
1306         // J�rg Schilling says that it is impossible to import the joliet tree for multisession
1307 //     if( !rootDir )
1308 //       rootDir = iso.firstJolietDirEntry();
1309         if( !rootDir )
1310             rootDir = iso.firstIsoDirEntry();
1311 
1312         if( rootDir ) {
1313             createSessionImportItems( rootDir, root() );
1314             emit changed();
1315             emit importedSessionChanged( importedSession() );
1316             return true;
1317         }
1318         else {
1319             qDebug() << "(K3b::DataDoc::importSession) Could not find primary volume desc.";
1320             return false;
1321         }
1322     }
1323     else {
1324         qDebug() << "(K3b::DataDoc) unable to read toc.";
1325         return false;
1326     }
1327 }
1328 
1329 
createSessionImportItems(const K3b::Iso9660Directory * importDir,K3b::DirItem * parent)1330 void K3b::DataDoc::createSessionImportItems( const K3b::Iso9660Directory* importDir, K3b::DirItem* parent )
1331 {
1332     if( !parent )
1333         return;
1334 
1335     Q_ASSERT(importDir);
1336     QStringList entries = importDir->entries();
1337     entries.removeAll( "." );
1338     entries.removeAll( ".." );
1339     for( QStringList::const_iterator it = entries.constBegin();
1340          it != entries.constEnd(); ++it ) {
1341         if( const K3b::Iso9660Entry* entry = importDir->entry( *it ) ) {
1342             K3b::DataItem* oldItem = parent->find( entry->name() );
1343             if( entry->isDirectory() ) {
1344                 K3b::DirItem* dir = 0;
1345                 if( oldItem && oldItem->isDir() ) {
1346                     dir = (K3b::DirItem*)oldItem;
1347                 }
1348                 else {
1349                     // we overwrite without warning!
1350                     if( oldItem )
1351                         removeItem( oldItem );
1352                     dir = new K3b::DirItem( entry->name() );
1353                     parent->addDataItem( dir );
1354                 }
1355 
1356                 dir->setRemoveable(false);
1357                 dir->setRenameable(false);
1358                 dir->setMoveable(false);
1359                 dir->setHideable(false);
1360                 dir->setWriteToCd(false);
1361                 dir->setExtraInfo( i18n("From previous session") );
1362                 d->oldSession.append( dir );
1363 
1364                 createSessionImportItems( static_cast<const K3b::Iso9660Directory*>(entry), dir );
1365             }
1366             else {
1367                 const K3b::Iso9660File* file = static_cast<const K3b::Iso9660File*>(entry);
1368 
1369                 // we overwrite without warning!
1370                 if( oldItem )
1371                     removeItem( oldItem );
1372 
1373                 K3b::SessionImportItem* item = new K3b::SessionImportItem( file );
1374                 item->setExtraInfo( i18n("From previous session") );
1375                 parent->addDataItem( item );
1376                 d->oldSession.append( item );
1377             }
1378         }
1379     }
1380 }
1381 
1382 
clearImportedSession()1383 void K3b::DataDoc::clearImportedSession()
1384 {
1385     //  d->oldSessionSizeHandler->clear();
1386     d->importedSession = -1;
1387     d->oldSessionSize = 0;
1388 
1389     while( !d->oldSession.isEmpty() ) {
1390         K3b::DataItem* item = d->oldSession.takeFirst();
1391 
1392         if( item->isDir() ) {
1393             K3b::DirItem* dir = (K3b::DirItem*)item;
1394             if( dir->numDirs() + dir->numFiles() == 0 ) {
1395                 // this imported dir is not needed anymore
1396                 // since it is empty
1397                 delete item;
1398             }
1399             else {
1400                 Q_FOREACH( K3b::DataItem* item, dir->children() ) {
1401                     if( !d->oldSession.contains( item ) ) {
1402                         // now the dir becomes a totally normal dir
1403                         dir->setRemoveable(true);
1404                         dir->setRenameable(true);
1405                         dir->setMoveable(true);
1406                         dir->setHideable(true);
1407                         dir->setWriteToCd(true);
1408                         dir->setExtraInfo( "" );
1409                         break;
1410                     }
1411                 }
1412             }
1413         }
1414         else {
1415             delete item;
1416         }
1417     }
1418 
1419     d->multisessionMode = AUTO;
1420 
1421     emit changed();
1422     emit importedSessionChanged( importedSession() );
1423 }
1424 
bootImages()1425 QList<K3b::BootItem*> K3b::DataDoc::bootImages()
1426 {
1427     return d->bootImages;
1428 }
1429 
1430 
bootCataloge()1431 K3b::DataItem* K3b::DataDoc::bootCataloge()
1432 {
1433     return d->bootCataloge;
1434 }
1435 
1436 
bootImageDir()1437 K3b::DirItem* K3b::DataDoc::bootImageDir()
1438 {
1439     K3b::DataItem* b = d->root->find( "boot" );
1440     if( !b ) {
1441         b = new K3b::DirItem( "boot" );
1442         d->root->addDataItem( b );
1443         setModified( true );
1444     }
1445 
1446     // if we cannot create the dir because there is a file named boot just use the root dir
1447     if( !b->isDir() )
1448         return d->root;
1449     else
1450         return static_cast<K3b::DirItem*>(b);
1451 }
1452 
1453 
createBootItem(const QString & filename,K3b::DirItem * dir)1454 K3b::BootItem* K3b::DataDoc::createBootItem( const QString& filename, K3b::DirItem* dir )
1455 {
1456     if( !dir )
1457         dir = bootImageDir();
1458 
1459     K3b::BootItem* boot = new K3b::BootItem( filename, *this );
1460     dir->addDataItem( boot );
1461 
1462     if( !d->bootCataloge )
1463         createBootCatalogeItem(dir);
1464 
1465     return boot;
1466 }
1467 
1468 
createBootCatalogeItem(K3b::DirItem * dir)1469 K3b::DataItem* K3b::DataDoc::createBootCatalogeItem( K3b::DirItem* dir )
1470 {
1471     if( !d->bootCataloge ) {
1472         QString newName = "boot.catalog";
1473         int i = 0;
1474         while( dir->alreadyInDirectory( "boot.catalog" ) ) {
1475             ++i;
1476             newName = QString( "boot%1.catalog" ).arg(i);
1477         }
1478 
1479         K3b::SpecialDataItem* b = new K3b::SpecialDataItem( 0, newName );
1480         dir->addDataItem( b );
1481         d->bootCataloge = b;
1482         d->bootCataloge->setRemoveable(false);
1483         d->bootCataloge->setHideable(false);
1484         d->bootCataloge->setWriteToCd(false);
1485         d->bootCataloge->setExtraInfo( i18n("El Torito boot catalog file") );
1486         b->setSpecialType( i18n("Boot catalog") );
1487     }
1488     else
1489         d->bootCataloge->reparent( dir );
1490 
1491     return d->bootCataloge;
1492 }
1493 
1494 
findItemByLocalPath(const QString & path) const1495 QList<K3b::DataItem*> K3b::DataDoc::findItemByLocalPath( const QString& path ) const
1496 {
1497     Q_UNUSED( path );
1498     return QList<K3b::DataItem*>();
1499 }
1500 
1501 
importedSession() const1502 int K3b::DataDoc::importedSession() const
1503 {
1504     return ( d->oldSession.isEmpty() ? -1 : d->importedSession );
1505 }
1506 
1507 
supportedMediaTypes() const1508 K3b::Device::MediaTypes K3b::DataDoc::supportedMediaTypes() const
1509 {
1510     return Device::MEDIA_WRITABLE;
1511 }
1512 
1513 
root() const1514 K3b::RootItem* K3b::DataDoc::root() const
1515 {
1516     return d->root;
1517 }
1518 
1519 
1520