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

0001 /*
0002     SPDX-FileCopyrightText: 2010 Michal Malek <michalm@jabster.pl>
0003     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "videodvd.h"
0008 
0009 #include <config-k3b.h>
0010 #include "k3bdevicemanager.h"
0011 #include "k3bdevice.h"
0012 #include "videodvd_export.h"
0013 #include "videodvd_log.h"
0014 #include "videodvd_i18n.h"
0015 
0016 #include <QCoreApplication>
0017 #include <QDateTime>
0018 #include <QBitArray>
0019 #include <QUrl>
0020 
0021 #include <stdlib.h>
0022 
0023 namespace
0024 {
0025     const int CMD_MIMETYPE = 70; // Should be declared in KIOCore/KIO/Global, but it's missing. Why?
0026 } // namespace
0027 
0028 using namespace KIO;
0029 
0030 // Pseudo plugin class to embed meta data
0031 class KIOPluginForMetaData : public QObject
0032 {
0033     Q_OBJECT
0034     Q_PLUGIN_METADATA(IID "org.kde.kio.worker.videodvd" FILE "videodvd.json")
0035 };
0036 
0037 extern "C"
0038 {
0039     VIDEODVD_EXPORT int kdemain( int argc, char **argv )
0040     {
0041         QCoreApplication app(argc, argv);
0042         QCoreApplication::setApplicationName( "kio_videodvd" );
0043 
0044         qCDebug(KIO_VIDEODVD_LOG) << "Starting";
0045 
0046         if (argc != 4)
0047         {
0048             fprintf(stderr, "Usage: kio_videodvd protocol domain-socket1 domain-socket2\n");
0049             exit(-1);
0050         }
0051 
0052         kio_videodvdProtocol worker(argv[2], argv[3]);
0053         worker.dispatchLoop();
0054 
0055         qCDebug(KIO_VIDEODVD_LOG) << "Done";
0056         return 0;
0057     }
0058 
0059     bool isRootDirectory( const QUrl& url )
0060     {
0061         QString path = url.path();
0062         return( path.isEmpty() || path == "/" );
0063     }
0064 }
0065 
0066 
0067 
0068 // FIXME: Does it really make sense to use a static device manager? Are all instances
0069 // of videodvd started in another process?
0070 K3b::Device::DeviceManager* kio_videodvdProtocol::s_deviceManager = nullptr;
0071 int kio_videodvdProtocol::s_instanceCnt = 0;
0072 
0073 kio_videodvdProtocol::kio_videodvdProtocol(const QByteArray &pool_socket, const QByteArray &app_socket)
0074     : WorkerBase("kio_videodvd", pool_socket, app_socket)
0075 {
0076     qCDebug(KIO_VIDEODVD_LOG) << "kio_videodvdProtocol::kio_videodvdProtocol()";
0077     if( !s_deviceManager )
0078     {
0079         s_deviceManager = new K3b::Device::DeviceManager();
0080         s_deviceManager->setCheckWritingModes( false );
0081         s_deviceManager->scanBus();
0082     }
0083     s_instanceCnt++;
0084 }
0085 
0086 
0087 kio_videodvdProtocol::~kio_videodvdProtocol()
0088 {
0089     qCDebug(KIO_VIDEODVD_LOG) << "kio_videodvdProtocol::~kio_videodvdProtocol()";
0090     s_instanceCnt--;
0091     if( s_instanceCnt == 0 )
0092     {
0093         delete s_deviceManager;
0094         s_deviceManager = nullptr;
0095     }
0096 }
0097 
0098 
0099 KIO::UDSEntry kio_videodvdProtocol::createUDSEntry( const K3b::Iso9660Entry* e ) const
0100 {
0101     KIO::UDSEntry uds;
0102     uds.fastInsert(KIO::UDSEntry::UDS_NAME,e->name());
0103     uds.fastInsert(KIO::UDSEntry::UDS_ACCESS, e->permissions());
0104     uds.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, e->date());
0105     uds.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME,e->date());
0106 
0107     if( e->isDirectory() )
0108     {
0109         uds.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0110         uds.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory");
0111     }
0112     else
0113     {
0114         const K3b::Iso9660File* file = static_cast<const K3b::Iso9660File*>( e );
0115         uds.fastInsert(KIO::UDSEntry::UDS_SIZE,file->size());
0116         uds.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
0117         QString iconName;
0118         if( e->name().endsWith( "VOB" ) )
0119             iconName = "video/mpeg";
0120         else
0121             iconName = "unknown";
0122         uds.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, iconName);
0123     }
0124 
0125     return uds;
0126 }
0127 
0128 
0129 // FIXME: remember the iso instance for quicker something and search for the videodvd
0130 //        in the available devices.
0131 KIO::WorkerResult kio_videodvdProtocol::openIso( const QUrl& url, std::unique_ptr<K3b::Iso9660>* isoPtr, QString* plainIsoPath )
0132 {
0133     // get the volume id from the url
0134     QString volumeId = url.path().section( '/', 1, 1 );
0135 
0136     qCDebug(KIO_VIDEODVD_LOG) << "(kio_videodvdProtocol) searching for Video dvd: " << volumeId;
0137 
0138 
0139     // now search the devices for this volume id
0140     // FIXME: use the cache created in listVideoDVDs
0141     QList<K3b::Device::Device *> items(s_deviceManager->dvdReader());
0142     for( QList<K3b::Device::Device *>::const_iterator it = items.constBegin();
0143          it != items.constEnd(); ++it ) {
0144         K3b::Device::Device* dev = *it;
0145         K3b::Device::DiskInfo di = dev->diskInfo();
0146 
0147         // we search for a DVD with a single track.
0148         // this time let K3b::Iso9660 decide if we need dvdcss or not
0149         // FIXME: check for encryption and libdvdcss and report an error
0150         if( K3b::Device::isDvdMedia( di.mediaType() ) && di.numTracks() == 1 ) {
0151             K3b::Iso9660* iso = new K3b::Iso9660( dev );
0152             iso->setPlainIso9660( true );
0153             if( iso->open() /*&& iso->primaryDescriptor().volumeId == volumeId*/ ) {
0154                 *plainIsoPath = url.path().section( '/', 2, -1 ) + '/';
0155                 (*isoPtr).reset(iso);
0156                 qCDebug(KIO_VIDEODVD_LOG) << "(kio_videodvdProtocol) using iso path: " << *plainIsoPath;
0157                 return KIO::WorkerResult::pass();
0158             }
0159             delete iso;
0160         }
0161     }
0162 
0163     return KIO::WorkerResult::fail( ERR_WORKER_DEFINED, i18n("No Video DVD found") );
0164 }
0165 
0166 
0167 KIO::WorkerResult kio_videodvdProtocol::get(const QUrl& url )
0168 {
0169     qCDebug(KIO_VIDEODVD_LOG) << "kio_videodvd::get(const QUrl& url)" << url;
0170 
0171     QString isoPath;
0172     std::unique_ptr<K3b::Iso9660> iso;
0173     const KIO::WorkerResult openIsoResult = openIso(url, &iso, &isoPath);
0174     if (!openIsoResult.success()) {
0175         return openIsoResult;
0176     }
0177 
0178     const K3b::Iso9660Entry* e = iso->firstIsoDirEntry()->entry( isoPath );
0179     if( e && e->isFile() )
0180     {
0181         const K3b::Iso9660File* file = static_cast<const K3b::Iso9660File*>( e );
0182         totalSize( file->size() );
0183         QByteArray buffer( 10*2048, '\n' );
0184         int read = 0;
0185         int cnt = 0;
0186         KIO::filesize_t totalRead = 0;
0187         while( (read = file->read( totalRead, buffer.data(), buffer.size() )) > 0 )
0188         {
0189             buffer.resize( read );
0190             data(buffer);
0191             ++cnt;
0192             totalRead += read;
0193             if( cnt == 10 )
0194             {
0195                 cnt = 0;
0196                 processedSize( totalRead );
0197             }
0198         }
0199 
0200         data(QByteArray()); // empty array means we're done sending the data
0201 
0202         if( read == 0 ) {
0203             return KIO::WorkerResult::pass();
0204         }
0205 
0206         return KIO::WorkerResult::fail( ERR_WORKER_DEFINED, i18n("Read error.") );
0207     }
0208 
0209     return KIO::WorkerResult::fail( ERR_DOES_NOT_EXIST, url.path() );
0210 }
0211 
0212 
0213 KIO::WorkerResult kio_videodvdProtocol::listDir( const QUrl& url )
0214 {
0215     qCDebug(KIO_VIDEODVD_LOG) << "kio_videodvd::listDir(const QUrl& url)" << url;
0216 
0217     if( isRootDirectory( url ) ) {
0218 #ifdef Q_OS_WIN32
0219     qCWarning(KIO_VIDEODVD_LOG) << "fix of root path required";
0220 #endif
0221         return listVideoDVDs();
0222     }
0223 
0224     QString isoPath;
0225     std::unique_ptr<K3b::Iso9660> iso;
0226     const KIO::WorkerResult openIsoResult = openIso(url, &iso, &isoPath);
0227     if (!openIsoResult.success()) {
0228         return openIsoResult;
0229     }
0230 
0231     const K3b::Iso9660Directory* mainDir = iso->firstIsoDirEntry();
0232     const K3b::Iso9660Entry* e = mainDir->entry( isoPath );
0233     if( e ) {
0234         if( e->isDirectory() ) {
0235             const K3b::Iso9660Directory* dir = static_cast<const K3b::Iso9660Directory*>(e);
0236             QStringList el = dir->entries();
0237             el.removeOne( "." );
0238             el.removeOne( ".." );
0239             UDSEntryList udsl;
0240             for( QStringList::const_iterator it = el.constBegin(); it != el.constEnd(); ++it )
0241                 udsl.append( createUDSEntry( dir->entry( *it ) ) );
0242             listEntries( udsl );
0243             return KIO::WorkerResult::pass();
0244         }
0245 
0246         return KIO::WorkerResult::fail( ERR_CANNOT_ENTER_DIRECTORY, url.path() );
0247     }
0248 
0249     return KIO::WorkerResult::fail( ERR_CANNOT_ENTER_DIRECTORY, url.path() );
0250 }
0251 
0252 
0253 KIO::WorkerResult kio_videodvdProtocol::listVideoDVDs()
0254 {
0255     UDSEntryList udsl;
0256 
0257     QList<K3b::Device::Device *> items(s_deviceManager->dvdReader());
0258     for( QList<K3b::Device::Device *>::const_iterator it = items.constBegin();
0259          it != items.constEnd(); ++it ) {
0260         K3b::Device::Device* dev = *it;
0261         K3b::Device::DiskInfo di = dev->diskInfo();
0262 
0263         // we search for a DVD with a single track.
0264         if( K3b::Device::isDvdMedia( di.mediaType() ) && di.numTracks() == 1 ) {
0265             //
0266             // now do a quick check for VideoDVD.
0267             // - no dvdcss for speed
0268             // - only a check for the VIDEO_TS dir
0269             //
0270             K3b::Iso9660 iso( new K3b::Iso9660DeviceBackend(dev) );
0271             iso.setPlainIso9660( true );
0272             if( iso.open() && iso.firstIsoDirEntry()->entry( "VIDEO_TS" ) != nullptr ) {
0273                 // FIXME: cache the entry for speedup
0274 
0275                 UDSEntry uds;
0276                 uds.fastInsert( KIO::UDSEntry::UDS_NAME,iso.primaryDescriptor().volumeId );
0277                 uds.fastInsert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
0278                 uds.fastInsert( KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory" );
0279                 uds.fastInsert( KIO::UDSEntry::UDS_ICON_NAME, "media-optical-video" );
0280                 uds.fastInsert( KIO::UDSEntry::UDS_SIZE, iso.primaryDescriptor().volumeSetSize );
0281 
0282                 udsl.append( uds );
0283                 listEntries( udsl );
0284             }
0285         }
0286     }
0287 
0288     if( !udsl.isEmpty() ) {
0289         return KIO::WorkerResult::pass();
0290     }
0291     return KIO::WorkerResult::fail( ERR_WORKER_DEFINED, i18n("No Video DVD found") );
0292 }
0293 
0294 
0295 KIO::WorkerResult kio_videodvdProtocol::stat( const QUrl& url )
0296 {
0297     qCDebug(KIO_VIDEODVD_LOG) << "kio_videodvd::stat(const QUrl& url)" << url;
0298 
0299     if( isRootDirectory( url ) ) {
0300 #ifdef Q_OS_WIN32
0301     qCWarning(KIO_VIDEODVD_LOG) << "fix root path detection";
0302 #endif
0303         //
0304         // stat the root path
0305         //
0306         KIO::UDSEntry uds;
0307         uds.fastInsert( KIO::UDSEntry::UDS_NAME, url.path() );
0308         uds.fastInsert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
0309         uds.fastInsert( KIO::UDSEntry::UDS_MIME_TYPE, "inode/directory" );
0310 
0311         statEntry( uds );
0312         return KIO::WorkerResult::pass();
0313     }
0314 
0315     QString isoPath;
0316     std::unique_ptr<K3b::Iso9660> iso;
0317     const KIO::WorkerResult openIsoResult = openIso(url, &iso, &isoPath);
0318     if (!openIsoResult.success()) {
0319         return openIsoResult;
0320     }
0321 
0322     const K3b::Iso9660Entry* e = iso->firstIsoDirEntry()->entry( isoPath );
0323     if( e ) {
0324         statEntry( createUDSEntry( e ) );
0325         return KIO::WorkerResult::pass();
0326     }
0327     return KIO::WorkerResult::fail( ERR_DOES_NOT_EXIST, url.path() );
0328 }
0329 
0330 
0331 // FIXME: when does this get called? It seems not to be used for the files.
0332 // This is called by KIO::mimetype, which is called e.g. when dropping an item onto
0333 // the "places" widget in the file dialog. Indeed not much used these days.
0334 // Note that you can also implement it as a get() without the "send the data"
0335 // part of it. (David)
0336 KIO::WorkerResult kio_videodvdProtocol::mimetype( const QUrl& url )
0337 {
0338     qCDebug(KIO_VIDEODVD_LOG) << "kio_videodvd::mimetype(const QUrl& url)" << url;
0339 
0340     if( isRootDirectory( url ) ) {
0341         return KIO::WorkerResult::fail( ERR_UNSUPPORTED_ACTION, KIO::unsupportedActionErrorString("videodvd", CMD_MIMETYPE) );
0342     }
0343 
0344     QString isoPath;
0345     std::unique_ptr<K3b::Iso9660> iso;
0346     const KIO::WorkerResult openIsoResult = openIso(url, &iso, &isoPath);
0347     if (!openIsoResult.success()) {
0348         return openIsoResult;
0349     }
0350 
0351     const K3b::Iso9660Entry* e = iso->firstIsoDirEntry()->entry( isoPath );
0352     if( e ) {
0353         if( e->isDirectory() ) {
0354             mimeType( "inode/directory" );
0355             return KIO::WorkerResult::pass();
0356         }
0357         if( e->name().endsWith( ".VOB" ) ) {
0358             mimeType( "video/mpeg" );
0359             return KIO::WorkerResult::pass();
0360         }
0361 
0362         // send some data
0363         const K3b::Iso9660File* file = static_cast<const K3b::Iso9660File*>( e );
0364         QByteArray buffer( 10*2048, '\n' );
0365         int read = file->read( 0, buffer.data(), buffer.size() );
0366         if( read > 0 )
0367         {
0368             buffer.resize( read );
0369             data(buffer);
0370             data(QByteArray());
0371             return KIO::WorkerResult::pass();
0372             // FIXME: do we need to emit finished() after emitting the end of data()?
0373         }
0374         return KIO::WorkerResult::fail( ERR_WORKER_DEFINED, i18n("Read error.") );
0375     }
0376     return KIO::WorkerResult::fail( ERR_DOES_NOT_EXIST, url.path() );
0377 }
0378 
0379 #include "videodvd.moc"