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

0001 /*
0002     SPDX-FileCopyrightText: 2009 Michal Malek <michalm@jabster.pl>
0003     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "k3bfileitem.h"
0009 #include "k3bdatadoc.h"
0010 #include "k3bdiritem.h"
0011 #include "k3bglobals.h"
0012 #include "k3bisooptions.h"
0013 #include <config-k3b.h>
0014 
0015 #include <QDebug>
0016 #include <QFile>
0017 #include <QFileInfo>
0018 #include <QMimeDatabase>
0019 #include <QRegularExpression>
0020 #include <QString>
0021 #include <QStringList>
0022 #include <QUrl>
0023 
0024 #include <errno.h>
0025 #include <string.h>
0026 
0027 
0028 bool K3b::operator==( const K3b::FileItem::Id& id1, const K3b::FileItem::Id& id2 )
0029 {
0030     return ( id1.device == id2.device && id1.inode == id2.inode );
0031 }
0032 
0033 
0034 bool K3b::operator<( const K3b::FileItem::Id& id1, const K3b::FileItem::Id& id2 )
0035 {
0036     if( id1.device == id2.device )
0037         return ( id1.inode < id2.inode );
0038     else
0039         return ( id1.device < id2.device );
0040 }
0041 
0042 
0043 bool K3b::operator>( const K3b::FileItem::Id& id1, const K3b::FileItem::Id& id2 )
0044 {
0045     return !( id2 < id1 || id1 == id2 );
0046 }
0047 
0048 
0049 
0050 K3b::FileItem::FileItem( const QString& filePath, K3b::DataDoc& doc, const QString& k3bName, const ItemFlags& flags )
0051     : K3b::DataItem( flags | FILE ),
0052       m_replacedItemFromOldSession(0),
0053       m_localPath(filePath)
0054 {
0055     k3b_struct_stat statBuf;
0056     k3b_struct_stat followedStatBuf;
0057     // we determine the size here to avoid problems with removed or renamed files
0058     // we need to use lstat here since for symlinks both KDE and QT return the size of the file pointed to
0059     // instead the size of the link.
0060     if( k3b_lstat( QFile::encodeName(filePath), &statBuf ) == 0 ) {
0061         if( k3b_stat( QFile::encodeName(filePath), &followedStatBuf ) == 0 ) {
0062             init( filePath, k3bName, doc, &statBuf, &followedStatBuf );
0063         }
0064         else {
0065             init( filePath, k3bName, doc, &statBuf, 0 );
0066             qCritical() << "(KFileItem) stat failed: " << QString::fromLocal8Bit( ::strerror(errno) ) << Qt::endl;
0067         }
0068     }
0069     else {
0070         qCritical() << "(KFileItem) lstat failed: " << QString::fromLocal8Bit( ::strerror(errno) ) << Qt::endl;
0071         if( k3b_stat( QFile::encodeName(filePath), &followedStatBuf ) == 0 ) {
0072             init( filePath, k3bName, doc, 0, &followedStatBuf );
0073         }
0074         else {
0075             init( filePath, k3bName, doc, 0, 0 );
0076             qCritical() << "(KFileItem) stat failed: " << QString::fromLocal8Bit( ::strerror(errno) ) << Qt::endl;
0077         }
0078     }
0079 }
0080 
0081 
0082 K3b::FileItem::FileItem( const k3b_struct_stat* stat,
0083                           const k3b_struct_stat* followedStat,
0084                           const QString& filePath, K3b::DataDoc& doc, const QString& k3bName, const ItemFlags& flags )
0085     : K3b::DataItem( flags | FILE ),
0086       m_replacedItemFromOldSession(0),
0087       m_localPath(filePath)
0088 {
0089     init( filePath, k3bName, doc, stat, followedStat );
0090 }
0091 
0092 
0093 K3b::FileItem::FileItem( const K3b::FileItem& item )
0094     : K3b::DataItem( item ),
0095       m_replacedItemFromOldSession(0),
0096       m_size( item.m_size ),
0097       m_sizeFollowed( item.m_sizeFollowed ),
0098       m_id( item.m_id ),
0099       m_idFollowed( item.m_idFollowed ),
0100       m_localPath( item.m_localPath ),
0101       m_mimeType( item.m_mimeType )
0102 {
0103 }
0104 
0105 
0106 K3b::FileItem::~FileItem()
0107 {
0108     // remove this from parentdir
0109     take();
0110 }
0111 
0112 
0113 K3b::DataItem* K3b::FileItem::copy() const
0114 {
0115     return new K3b::FileItem( *this );
0116 }
0117 
0118 
0119 QMimeType K3b::FileItem::mimeType() const
0120 {
0121     return m_mimeType;
0122 }
0123 
0124 
0125 KIO::filesize_t K3b::FileItem::itemSize( bool followSymlinks ) const
0126 {
0127     if( followSymlinks )
0128         return m_sizeFollowed;
0129     else
0130         return m_size;
0131 }
0132 
0133 
0134 K3b::FileItem::Id K3b::FileItem::localId() const
0135 {
0136     if( DataDoc* doc = getDoc() )
0137         return localId( doc->isoOptions().followSymbolicLinks() || !doc->isoOptions().createRockRidge() );
0138     else
0139         return localId( false );
0140 }
0141 
0142 
0143 K3b::FileItem::Id K3b::FileItem::localId( bool followSymlinks ) const
0144 {
0145     if( followSymlinks )
0146         return m_idFollowed;
0147     else
0148         return m_id;
0149 }
0150 
0151 
0152 bool K3b::FileItem::exists() const
0153 {
0154     return true;
0155 }
0156 
0157 QString K3b::FileItem::absIsoPath()
0158 {
0159     //  return m_dir->absIsoPath() + m_isoName;
0160     return QString();
0161 }
0162 
0163 
0164 QString K3b::FileItem::localPath() const
0165 {
0166     return m_localPath;
0167 }
0168 
0169 
0170 K3b::DirItem* K3b::FileItem::getDirItem() const
0171 {
0172     return parent();
0173 }
0174 
0175 
0176 QString K3b::FileItem::linkDest() const
0177 {
0178     return QFileInfo( localPath() ).symLinkTarget();
0179 }
0180 
0181 
0182 bool K3b::FileItem::isValid() const
0183 {
0184     if( isSymLink() ) {
0185 
0186         // this link is not valid if we cannot follow it if we want to
0187         if( DataDoc* doc = getDoc() ) {
0188             if( doc->isoOptions().followSymbolicLinks() ) {
0189                 return QFile::exists( K3b::resolveLink( localPath() ) );
0190             }
0191         }
0192 
0193         QString dest = linkDest();
0194 
0195         if( dest[0] == '/' )
0196             return false;  // absolute links can never be part of the compilation!
0197 
0198         // parse the link
0199         K3b::DirItem* dir = parent();
0200 
0201         static const QRegularExpression rx("/+");
0202         QStringList tokens = dest.split( rx );  // two slashes or more do the same as one does!
0203 
0204         int i = 0;
0205         while( i < tokens.size() ) {
0206             if( tokens[i] == "." ) {
0207                 // ignore it
0208             }
0209             else if( tokens[i] == ".." ) {
0210                 // change the directory
0211                 dir = dir->parent();
0212                 if( dir == 0 )
0213                     return false;
0214             }
0215             else {
0216                 // search for the item in dir
0217                 K3b::DataItem* d = dir->find( tokens[i] );
0218                 if( d == 0 )
0219                     return false;
0220 
0221                 if( d->isDir() ) {
0222                     // change directory
0223                     dir = (K3b::DirItem*)d;
0224                 }
0225                 else {
0226                     if( i+1 != tokens.size() )
0227                         return false;  // if di is a file we need to be at the last token
0228                     else
0229                         return (dest[dest.length()-1] != '/');   // if the link destination ends with a slash
0230                     // it can only point to a directory!
0231                 }
0232             }
0233 
0234             i++;
0235         }
0236 
0237         return true;
0238     }
0239     else
0240         return true;
0241 }
0242 
0243 
0244 void K3b::FileItem::init( const QString& filePath,
0245                           const QString& k3bName,
0246                           DataDoc& doc,
0247                           const k3b_struct_stat* stat,
0248                           const k3b_struct_stat* followedStat )
0249 {
0250     if( k3bName.isEmpty() )
0251         m_k3bName = filePath.section( '/', -1 );
0252     else
0253         m_k3bName = k3bName;
0254 
0255     if( stat != 0 ) {
0256         m_size = (KIO::filesize_t)stat->st_size;
0257         if( S_ISLNK(stat->st_mode) )
0258             setFlags( flags() | SYMLINK );
0259 
0260         //
0261         // integrate the device number into the inode since files on different
0262         // devices may have the same inode number!
0263         //
0264         m_id.inode = stat->st_ino;
0265         m_id.device = stat->st_dev;
0266     }
0267     else {
0268         m_size = QFileInfo(filePath).size();
0269         m_id.inode = 0;
0270         m_id.device = 0;
0271 
0272         // since we have no proper inode info, disable the inode caching in the doc
0273         K3b::IsoOptions o( doc.isoOptions() );
0274         o.setDoNotCacheInodes( true );
0275         doc.setIsoOptions( o );
0276     }
0277 
0278     if( isSymLink() ) {
0279         if( QFile::exists( K3b::resolveLink( filePath ) ) && followedStat != 0 ) {
0280             m_sizeFollowed = (KIO::filesize_t)followedStat->st_size;
0281             m_idFollowed.inode = followedStat->st_ino;
0282             m_idFollowed.device = followedStat->st_dev;
0283         }
0284         else if( followedStat == 0 ) {
0285             m_sizeFollowed = m_size;
0286             m_idFollowed.inode = 0;
0287             m_idFollowed.device = 0;
0288         }
0289         else {
0290             // This means the link is broken, so size of target equals 0
0291             m_sizeFollowed = 0;
0292         }
0293     }
0294     else {
0295         m_sizeFollowed = m_size;
0296         m_idFollowed = m_id;
0297     }
0298 
0299     m_mimeType = QMimeDatabase().mimeTypeForFile( filePath );
0300 
0301     // add automagically like a qlistviewitem
0302     if( parent() )
0303         parent()->addDataItem( this );
0304 }