File indexing completed on 2024-05-19 08:41:55
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"