diff --git a/mtp/solid_mtp.desktop b/mtp/solid_mtp.desktop index 39db22a0..2f840882 100644 --- a/mtp/solid_mtp.desktop +++ b/mtp/solid_mtp.desktop @@ -1,35 +1,36 @@ [Desktop Entry] X-KDE-Solid-Predicate=PortableMediaPlayer.supportedProtocols == 'mtp' Type=Service Actions=open; [Desktop Action open] Exec=kioclient5 exec mtp:udi=%i/ Icon=system-file-manager Name=Browse Files with File Manager Name[ca]=Navega pels fitxers amb el gestor de fitxers Name[ca@valencia]=Navega pels fitxers amb el gestor de fitxers Name[cs]=Prohlížet soubory ve správci souborů Name[de]=Dateien mit Dateiverwaltung durchsuchen Name[el]=Περιήγηση σε αρχεία με τον διαχειριστή αρχείων Name[en_GB]=Browse Files with File Manager Name[es]=Explorar archivos con el gestor de archivos Name[et]=Failide sirvimine failihalduriga Name[eu]=Arakatu fitxategiak fitxategi kudeatzailearekin Name[fr]=Parcourir les fichiers avec le gestionnaire de fichiers Name[gl]=Explorar o ficheiros co xestor de ficheiros +Name[ia]=Naviga per le files con le Gerente de File Name[it]=Sfoglia file con il gestore dei file Name[ja]=ファイルマネージャでファイルを閲覧 Name[ko]=파일 관리자로 파일 탐색 Name[lt]=Naršyti failus naudojant failų tvarkytuvę Name[nl]=Door bestanden bladeren met bestandsbeheerder Name[nn]=Bla gjennom filer med filhandsamaren Name[pl]=Otwórz w przeglądarce plików Name[pt]=Navegar com o Gestor de Ficheiros Name[pt_BR]=Navegar com o gerenciador de arquivos Name[sk]=Prehliadať Súbory zo Správcom Súborov Name[sv]=Bläddra bland filer med filhanterare Name[uk]=Переглянути файли у програмі для керування файлами Name[x-test]=xxBrowse Files with File Managerxx Name[zh_CN]=使用文件管理器浏览文件 Name[zh_TW]=使用檔案管理員瀏覽檔案 diff --git a/smb/kio_smb_browse.cpp b/smb/kio_smb_browse.cpp index a8ee2f8a..c90df37e 100644 --- a/smb/kio_smb_browse.cpp +++ b/smb/kio_smb_browse.cpp @@ -1,673 +1,694 @@ ///////////////////////////////////////////////////////////////////////////// // // Project: SMB kioslave for KDE2 // // File: kio_smb_browse.cpp // // Abstract: member function implementations for SMBSlave that deal with // SMB browsing // // Author(s): Matthew Peterson // //--------------------------------------------------------------------------- // // Copyright (c) 2000 Caldera Systems, Inc. // Copyright (c) 2018 Harald Sitter // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2.1 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; see the file COPYING. If not, please obtain // a copy from https://www.gnu.org/copyleft/gpl.html // ///////////////////////////////////////////////////////////////////////////// #include "kio_smb.h" #include "kio_smb_internal.h" #include #include #include #include #include #include #include #include using namespace KIO; int SMBSlave::cache_stat(const SMBUrl &url, struct stat* st ) { int cacheStatErr; int result = smbc_stat( url.toSmbcUrl(), st); if (result == 0){ cacheStatErr = 0; } else { cacheStatErr = errno; } qCDebug(KIO_SMB_LOG) << "size " << (KIO::filesize_t)st->st_size; return cacheStatErr; } //--------------------------------------------------------------------------- int SMBSlave::browse_stat_path(const SMBUrl& _url, UDSEntry& udsentry) { SMBUrl url = _url; int cacheStatErr = cache_stat(url, &st); if(cacheStatErr == 0) { if(!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) { qCDebug(KIO_SMB_LOG) << "mode: "<< st.st_mode; warning(i18n("%1:\n" "Unknown file type, neither directory or file.", url.toDisplayString())); return EINVAL; } + if (!S_ISDIR(st.st_mode)) { + // Awkwardly documented at + // https://www.samba.org/samba/docs/using_samba/ch08.html + // libsmb_stat.c assigns special meaning to +x permissions + // (obviously only on files, all dirs are +x so this hacky representation + // wouldn't work!): + // - S_IXUSR = DOS archive: This file has been touched since the last DOS backup was performed on it. + // - S_IXGRP = DOS system: This file has a specific purpose required by the operating system. + // - S_IXOTH = DOS hidden: This file has been marked to be invisible to the user, unless the operating system is explicitly set to show it. + // Only hiding has backing through KIO right now. + if (st.st_mode & S_IXOTH) { // DOS hidden + udsentry.fastInsert(KIO::UDSEntry::UDS_HIDDEN, true); + } + } + udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, st.st_mode & S_IFMT); udsentry.fastInsert(KIO::UDSEntry::UDS_SIZE, st.st_size); - QString str; - uid_t uid = st.st_uid; - struct passwd *user = getpwuid( uid ); - if ( user ) - str = user->pw_name; - else - str = QString::number( uid ); - udsentry.fastInsert(KIO::UDSEntry::UDS_USER, str); - - gid_t gid = st.st_gid; - struct group *grp = getgrgid( gid ); - if ( grp ) - str = grp->gr_name; - else - str = QString::number( gid ); - udsentry.fastInsert(KIO::UDSEntry::UDS_GROUP, str); + // UID and GID **must** not be mapped. The values returned by libsmbclient are + // simply the getuid/getgid of the process. They mean absolutely nothing. + // Also see libsmb_stat.c. + // Related: https://bugs.kde.org/show_bug.cgi?id=212801 udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, st.st_mode & 07777); udsentry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, st.st_mtime); udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, st.st_atime); // No, st_ctime is not UDS_CREATION_TIME... } return cacheStatErr; } //=========================================================================== void SMBSlave::stat( const QUrl& kurl ) { qCDebug(KIO_SMB_LOG) << kurl; // make a valid URL QUrl url = checkURL(kurl); // if URL is not valid we have to redirect to correct URL if (url != kurl) { qCDebug(KIO_SMB_LOG) << "redirection " << url; redirection(url); finished(); return; } m_current_url = url; UDSEntry udsentry; // Set name udsentry.fastInsert( KIO::UDSEntry::UDS_NAME, kurl.fileName() ); switch(m_current_url.getType()) { case SMBURLTYPE_UNKNOWN: error(ERR_MALFORMED_URL, url.toDisplayString()); return; case SMBURLTYPE_ENTIRE_NETWORK: case SMBURLTYPE_WORKGROUP_OR_SERVER: udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); break; case SMBURLTYPE_SHARE_OR_PATH: { int ret = browse_stat_path(m_current_url, udsentry); if (ret == EPERM || ret == EACCES || workaroundEEXIST(ret)) { SMBUrl smbUrl(url); const int passwordError = checkPassword(smbUrl); if (passwordError == KJob::NoError) { redirection(smbUrl); finished(); } else if (passwordError == KIO::ERR_USER_CANCELED) { reportError(url, ret); } else { error(passwordError, url.toString()); } return; } else if (ret != 0) { qCDebug(KIO_SMB_LOG) << "stat() error" << ret << url; reportError(url, ret); return; } break; } default: qCDebug(KIO_SMB_LOG) << "UNKNOWN " << url; finished(); return; } statEntry(udsentry); finished(); } //=========================================================================== -// TODO: complete checking -QUrl SMBSlave::checkURL(const QUrl& kurl) const +// TODO: complete checking <-- what does that even mean? +// TODO: why is this not part of SMBUrl or at the very least URL validation should +// be 100% shared between this and SMBUrl. Notably SMBUrl has code that looks +// to do a similar thing but is much less complete. +QUrl SMBSlave::checkURL(const QUrl &kurl_) const { - qCDebug(KIO_SMB_LOG) << "checkURL " << kurl; + qCDebug(KIO_SMB_LOG) << "checkURL " << kurl_; + + QUrl kurl(kurl_); + // We treat cifs as an alias but need to translate it to smb. + // https://bugs.kde.org/show_bug.cgi?id=327295 + // It's not IANA registered and also libsmbc internally expects + // smb URIs so we do very broadly coerce cifs to smb. + // Also see SMBUrl. + if (kurl.scheme() == "cifs") { + kurl.setScheme("smb"); + } + QString surl = kurl.url(); //transform any links in the form smb:/ into smb:// if (surl.startsWith(QLatin1String("smb:/"))) { if (surl.length() == 5) { return QUrl("smb://"); } if (surl.at(5) != '/') { surl = "smb://" + surl.mid(5); qCDebug(KIO_SMB_LOG) << "checkURL return1 " << surl << " " << QUrl(surl); return QUrl(surl); } } if (surl == QLatin1String("smb://")) { return kurl; //unchanged } // smb:// normally have no userinfo // we must redirect ourself to remove the username and password if (surl.contains('@') && !surl.contains("smb://")) { QUrl url(kurl); url.setPath('/'+kurl.url().right( kurl.url().length()-kurl.url().indexOf('@') -1)); QString userinfo = kurl.url().mid(5, kurl.url().indexOf('@')-5); if(userinfo.contains(':')) { url.setUserName(userinfo.left(userinfo.indexOf(':'))); url.setPassword(userinfo.right(userinfo.length()-userinfo.indexOf(':')-1)); } else { url.setUserName(userinfo); } qCDebug(KIO_SMB_LOG) << "checkURL return2 " << url; return url; } //if there's a valid host, don't have an empty path QUrl url(kurl); if (url.path().isEmpty()) url.setPath("/"); qCDebug(KIO_SMB_LOG) << "checkURL return3 " << url; return url; } SMBSlave::SMBError SMBSlave::errnumToKioError(const SMBUrl &url, const int errNum) { qCDebug(KIO_SMB_LOG) << "errNum" << errNum; switch(errNum) { case ENOENT: if (url.getType() == SMBURLTYPE_ENTIRE_NETWORK) return SMBError{ ERR_SLAVE_DEFINED, i18n("Unable to find any workgroups in your local network. This might be caused by an enabled firewall.") }; else return SMBError{ ERR_DOES_NOT_EXIST, url.toDisplayString() }; #ifdef ENOMEDIUM case ENOMEDIUM: return SMBError{ ERR_SLAVE_DEFINED, i18n("No media in device for %1", url.toDisplayString()) }; #endif #ifdef EHOSTDOWN case EHOSTDOWN: #endif case ECONNREFUSED: return SMBError{ ERR_SLAVE_DEFINED, i18n("Could not connect to host for %1", url.toDisplayString()) }; break; case ENOTDIR: return SMBError{ ERR_CANNOT_ENTER_DIRECTORY, url.toDisplayString() }; case EFAULT: case EINVAL: return SMBError{ ERR_DOES_NOT_EXIST, url.toDisplayString() }; case EPERM: case EACCES: return SMBError{ ERR_ACCESS_DENIED, url.toDisplayString() }; case EIO: case ENETUNREACH: if ( url.getType() == SMBURLTYPE_ENTIRE_NETWORK || url.getType() == SMBURLTYPE_WORKGROUP_OR_SERVER ) return SMBError{ ERR_SLAVE_DEFINED, i18n("Error while connecting to server responsible for %1", url.toDisplayString()) }; else return SMBError{ ERR_CONNECTION_BROKEN, url.toDisplayString() }; case ENOMEM: return SMBError{ ERR_OUT_OF_MEMORY, url.toDisplayString() }; case ENODEV: return SMBError{ ERR_SLAVE_DEFINED, i18n("Share could not be found on given server") }; case EBADF: return SMBError{ ERR_INTERNAL, i18n("Bad file descriptor") }; case ETIMEDOUT: return SMBError{ ERR_SERVER_TIMEOUT, url.host() }; case ENOTEMPTY: return SMBError{ ERR_CANNOT_RMDIR, url.toDisplayString() }; #ifdef ENOTUNIQ case ENOTUNIQ: return SMBError{ ERR_SLAVE_DEFINED, i18n("The given name could not be resolved to a unique server. " "Make sure your network is setup without any name conflicts " "between names used by Windows and by UNIX name resolution." ) }; #endif case 0: // success return SMBError{ ERR_INTERNAL, i18n("libsmbclient reported an error, but did not specify " "what the problem is. This might indicate a severe problem " "with your network - but also might indicate a problem with " "libsmbclient.\n" "If you want to help us, please provide a tcpdump of the " "network interface while you try to browse (be aware that " "it might contain private data, so do not post it if you are " "unsure about that - you can send it privately to the developers " "if they ask for it)") }; default: return SMBError{ ERR_INTERNAL, i18n("Unknown error condition in stat: %1", QString::fromLocal8Bit( strerror(errNum))) }; } } void SMBSlave::reportError(const SMBUrl& url, const int errNum) { const SMBError smbErr = errnumToKioError(url, errNum); error(smbErr.kioErrorId, smbErr.errorString); } void SMBSlave::reportWarning(const SMBUrl& url, const int errNum) { const SMBError smbErr = errnumToKioError(url, errNum); const QString errorString = buildErrorString(smbErr.kioErrorId, smbErr.errorString); warning(xi18n("Error occurred while trying to access %1%2", url.url(), errorString)); } //=========================================================================== void SMBSlave::listDir( const QUrl& kurl ) { qCDebug(KIO_SMB_LOG) << kurl; int errNum = 0; // check (correct) URL QUrl url = checkURL(kurl); // if URL is not valid we have to redirect to correct URL if (url != kurl) { redirection(url); finished(); return; } m_current_url = kurl; int dirfd; struct smbc_dirent *dirp = nullptr; UDSEntry udsentry; bool dir_is_root = true; dirfd = smbc_opendir( m_current_url.toSmbcUrl() ); if (dirfd > 0){ errNum = 0; } else { errNum = errno; } qCDebug(KIO_SMB_LOG) << "open " << m_current_url.toSmbcUrl() << " " << m_current_url.getType() << " " << dirfd; if(dirfd >= 0) { uint direntCount = 0; do { qCDebug(KIO_SMB_LOG) << "smbc_readdir "; dirp = smbc_readdir(dirfd); if(dirp == nullptr) break; ++direntCount; // Set name QString udsName; const QString dirpName = QString::fromUtf8( dirp->name ); // We cannot trust dirp->commentlen has it might be with or without the NUL character // See KDE bug #111430 and Samba bug #3030 const QString comment = QString::fromUtf8( dirp->comment ); if ( dirp->smbc_type == SMBC_SERVER || dirp->smbc_type == SMBC_WORKGROUP ) { udsName = dirpName.toLower(); udsName[0] = dirpName.at( 0 ).toUpper(); if ( !comment.isEmpty() && dirp->smbc_type == SMBC_SERVER ) udsName += " (" + comment + ')'; } else udsName = dirpName; qCDebug(KIO_SMB_LOG) << "dirp->name " << dirp->name << " " << dirpName << " '" << comment << "'" << " " << dirp->smbc_type; udsentry.fastInsert( KIO::UDSEntry::UDS_NAME, udsName ); + udsentry.fastInsert(KIO::UDSEntry::UDS_COMMENT, QString::fromUtf8(dirp->comment)); // Mark all administrative shares, e.g ADMIN$, as hidden. #197903 if (dirpName.endsWith(QLatin1Char('$'))) { //qCDebug(KIO_SMB_LOG) << dirpName << "marked as hidden"; udsentry.fastInsert(KIO::UDSEntry::UDS_HIDDEN, 1); } if (udsName == ".") { // Skip the "." entry // Mind the way m_current_url is handled in the loop } else if (udsName == "..") { dir_is_root = false; // fprintf(stderr,"----------- hide: -%s-\n",dirp->name); // do nothing and hide the hidden shares } else if (dirp->smbc_type == SMBC_FILE || dirp->smbc_type == SMBC_DIR) { // Set stat information m_current_url.addPath(dirpName); const int statErr = browse_stat_path(m_current_url, udsentry); if (statErr) { if (statErr == ENOENT || statErr == ENOTDIR) { reportWarning(m_current_url, statErr); } } else { // Call base class to list entry listEntry(udsentry); } m_current_url.cd(".."); } else if(dirp->smbc_type == SMBC_SERVER || dirp->smbc_type == SMBC_FILE_SHARE) { // Set type udsentry.fastInsert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR ); if (dirp->smbc_type == SMBC_SERVER) { udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); // QString workgroup = m_current_url.host().toUpper(); QUrl u("smb://"); u.setHost(dirpName); // when libsmbclient knows // u = QString("smb://%1?WORKGROUP=%2").arg(dirpName).arg(workgroup.toUpper()); qCDebug(KIO_SMB_LOG) << "list item " << u; udsentry.fastInsert(KIO::UDSEntry::UDS_URL, u.url()); udsentry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("application/x-smb-server")); } else udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)); // Call base class to list entry listEntry(udsentry); } else if(dirp->smbc_type == SMBC_WORKGROUP) { // Set type udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); // Set permissions udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH)); udsentry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("application/x-smb-workgroup")); // QString workgroup = m_current_url.host().toUpper(); QUrl u("smb://"); u.setHost(dirpName); udsentry.fastInsert(KIO::UDSEntry::UDS_URL, u.url()); // Call base class to list entry listEntry(udsentry); } else { qCDebug(KIO_SMB_LOG) << "SMBC_UNKNOWN :" << dirpName; // TODO: we don't handle SMBC_IPC_SHARE, SMBC_PRINTER_SHARE // SMBC_LINK, SMBC_COMMS_SHARE //SlaveBase::error(ERR_INTERNAL, TEXT_UNSUPPORTED_FILE_TYPE); // continue; } udsentry.clear(); } while (dirp); // checked already in the head listDNSSD(udsentry, url, direntCount); if (dir_is_root) { udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); udsentry.fastInsert(KIO::UDSEntry::UDS_NAME, "."); udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH)); } else { udsentry.fastInsert(KIO::UDSEntry::UDS_NAME, "."); const int statErr = browse_stat_path(m_current_url, udsentry); if (statErr) { if (statErr == ENOENT || statErr == ENOTDIR) { reportWarning(m_current_url, statErr); } // Create a default UDSEntry if we could not stat the actual directory udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)); } } listEntry(udsentry); udsentry.clear(); // clean up smbc_closedir(dirfd); } else { if (errNum == EPERM || errNum == EACCES || workaroundEEXIST(errNum)) { const int passwordError = checkPassword(m_current_url); if (passwordError == KJob::NoError) { redirection( m_current_url ); finished(); } else if (passwordError == KIO::ERR_USER_CANCELED) { reportError(m_current_url, errNum); } else { error(passwordError, m_current_url.toString()); } return; } reportError(m_current_url, errNum); return; } finished(); } void SMBSlave::listDNSSD(UDSEntry &udsentry, const QUrl &url, const uint direntCount) { // Certain versions of KDNSSD suffer from signal races which can easily // deadlock the slave. #ifndef HAVE_KDNSSD_WITH_SIGNAL_RACE_PROTECTION return; #endif // HAVE_KDNSSD_WITH_SIGNAL_RACE_PROTECTION // This entire method act as fallback logic iff SMB discovery is not working // (for example when using a protocol version that doesn't have discovery). // As such we can return if entries were discovered or the URL is not '/' auto normalizedUrl = url.adjusted(QUrl::NormalizePathSegments); if (direntCount > 0 || !normalizedUrl.path().isEmpty()) { return; } // Slaves have no event loop, start one for the poll. // KDNSSD has an internal timeout which may trigger if this takes too long // so in theory this should not ever be able to get stuck. // The eventloop runs until the discovery is finished. The finished slot // will quit it. QList services; QEventLoop e; KDNSSD::ServiceBrowser browser(QStringLiteral("_smb._tcp")); connect(&browser, &KDNSSD::ServiceBrowser::serviceAdded, this, [&services](KDNSSD::RemoteService::Ptr service){ qCDebug(KIO_SMB_LOG) << "DNSSD added:" << service->serviceName() << service->type() << service->domain() << service->hostName() << service->port(); // Manual contains check. We need to use the == of the underlying // objects, not the pointers. The same service may have >1 // RemoteService* instances representing it, so the == impl of // RemoteService::Ptr is useless here. for (const auto &it : services) { if (*service == *it) { return; } } // Schedule resolution of hostname. We'll later call resolve // which will block until the resolution is done. This basically // gives us a head start. service->resolveAsync(); services.append(service); }); connect(&browser, &KDNSSD::ServiceBrowser::serviceRemoved, this, [&services](KDNSSD::RemoteService::Ptr service){ qCDebug(KIO_SMB_LOG) << "DNSSD removed:" << service->serviceName() << service->type() << service->domain() << service->hostName() << service->port(); services.removeAll(service); }); connect(&browser, &KDNSSD::ServiceBrowser::finished, this, [&]() { browser.disconnect(); // Stop sending anything, we'll exit here. // Resolution still requires an event loop. So, let the resolutions // finish and then quit the loop. Services that fail resolution // get dropped since we won't be able to access them properly. for (auto it = services.begin(); it != services.end(); ++it) { auto service = *it; if (!service->resolve()) { qCWarning(KIO_SMB_LOG) << "Failed to resolve DNSSD service" << service->serviceName(); it = services.erase(it); } } e.quit(); }); browser.startBrowse(); e.exec(); for (const auto &service : services) { udsentry.fastInsert(KIO::UDSEntry::UDS_NAME, service->serviceName()); udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); // TODO: it may be better to resolve the host to an ip address. dnssd // being able to find a service doesn't mean name resolution is // properly set up for its domain. So, we may not be able to resolve // this without help from avahi. OTOH KDNSSD doesn't have API for this // and from a platform POV we should probably assume that if avahi // is functional it is also set up as resolution provider. // Given the plugin design on glibc's libnss however I am not sure // that assumption will be true all the time. ~sitter, 2018 QUrl u(QStringLiteral("smb://")); u.setHost(service->hostName()); if (service->port() > 0 && service->port() != 445 /* default smb */) { u.setPort(service->port()); } udsentry.fastInsert(KIO::UDSEntry::UDS_URL, u.url()); udsentry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("application/x-smb-server")); listEntry(udsentry); udsentry.clear(); } // NOTE: workgroups cannot be properly resolved because libsmbclient // seems to lack the appropriate API to pull this data out of netbios. // Netbios is also not working on IPv6 and its replacement (LLMNR) // doesn't support the concept of workgroups. } void SMBSlave::fileSystemFreeSpace(const QUrl& url) { qCDebug(KIO_SMB_LOG) << url; // Avoid crashing in smbc_fstatvfs below when // requesting free space for smb:// which doesn't // make sense to do to begin with if (url.host().isEmpty()) { error(KIO::ERR_CANNOT_STAT, url.url()); return; } SMBUrl smbcUrl = url; - int handle = smbc_opendir(smbcUrl.toSmbcUrl()); - if (handle < 0) { - error(KIO::ERR_CANNOT_STAT, url.url()); - return; - } struct statvfs dirStat; memset(&dirStat, 0, sizeof(struct statvfs)); - int err = smbc_fstatvfs(handle, &dirStat); - smbc_closedir(handle); - + const int err = smbc_statvfs(smbcUrl.toSmbcUrl().data(), &dirStat); if (err < 0) { error(KIO::ERR_CANNOT_STAT, url.url()); return; } - KIO::filesize_t blockSize; - if (dirStat.f_frsize != 0) { - blockSize = dirStat.f_frsize; - } else { - blockSize = dirStat.f_bsize; - } - - setMetaData("total", QString::number(blockSize * dirStat.f_blocks)); - setMetaData("available", QString::number(blockSize * dirStat.f_bavail)); + // libsmb_stat.c has very awkward conditional branching that results + // in data meaning different things based on context: + // A samba host with unix extensions has f_frsize==0 and the f_bsize is + // the actual block size. Any other server (such as windows) has a non-zero + // f_frsize denoting the amount of sectors in a block and the f_bsize is + // the amount of bytes in a sector. As such frsize*bsize is the actual + // block size. + // This was also broken in different ways throughout history, so depending + // on the specific libsmbc versions the milage will vary. 4.7 to 4.11 are + // at least behaving as described though. + // https://bugs.kde.org/show_bug.cgi?id=298801 + const auto frames = (dirStat.f_frsize == 0) ? 1 : dirStat.f_frsize; + const auto blockSize = dirStat.f_bsize * frames; + // Further more on older versions of samba f_bavail may not be set... + const auto total = blockSize * dirStat.f_blocks; + const auto available = blockSize * ((dirStat.f_bavail != 0) ? dirStat.f_bavail : dirStat.f_bfree); + + setMetaData("total", QString::number(total)); + setMetaData("available", QString::number(available)); finished(); } bool SMBSlave::workaroundEEXIST(const int errNum) const { return (errNum == EEXIST) && m_enableEEXISTWorkaround; } diff --git a/smb/kio_smb_dir.cpp b/smb/kio_smb_dir.cpp index 43ca02b1..29beed6a 100644 --- a/smb/kio_smb_dir.cpp +++ b/smb/kio_smb_dir.cpp @@ -1,784 +1,784 @@ ///////////////////////////////////////////////////////////////////////////// // // Project: SMB kioslave for KDE2 // // File: kio_smb_dir.cpp // // Abstract: member function implementations for SMBSlave that deal with // SMB directory access // // Author(s): Matthew Peterson // ////--------------------------------------------------------------------------- // // Copyright (c) 2000 Caldera Systems, Inc. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2.1 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; see the file COPYING. If not, please obtain // a copy from https://www.gnu.org/copyleft/gpl.html // ///////////////////////////////////////////////////////////////////////////// #include "kio_smb.h" #include "kio_smb_internal.h" #include #include #include #include #include //=========================================================================== void SMBSlave::copy(const QUrl& src, const QUrl& dst, int permissions, KIO::JobFlags flags) { const bool isSourceLocal = src.isLocalFile(); const bool isDestinationLocal = dst.isLocalFile(); if (!isSourceLocal && isDestinationLocal) { smbCopyGet(src, dst, permissions, flags); } else if (isSourceLocal && !isDestinationLocal) { smbCopyPut(src, dst, permissions, flags); } else { smbCopy(src, dst, permissions, flags); } } void SMBSlave::smbCopy(const QUrl& ksrc, const QUrl& kdst, int permissions, KIO::JobFlags flags) { SMBUrl src; SMBUrl dst; mode_t initialmode; ssize_t n; int dstflags; int srcfd = -1; int dstfd = -1; int errNum = 0; KIO::filesize_t processed_size = 0; unsigned char buf[MAX_XFER_BUF_SIZE]; qCDebug(KIO_SMB_LOG) << "SMBSlave::copy with src = " << ksrc << "and dest = " << kdst; // setup urls src = ksrc; dst = kdst; // Obtain information about source errNum = cache_stat(src, &st ); if( errNum != 0 ) { if ( errNum == EACCES ) { error( KIO::ERR_ACCESS_DENIED, src.toDisplayString()); } else { error( KIO::ERR_DOES_NOT_EXIST, src.toDisplayString()); } return; } if ( S_ISDIR( st.st_mode ) ) { error( KIO::ERR_IS_DIRECTORY, src.toDisplayString() ); return; } totalSize(st.st_size); // Check to se if the destination exists errNum = cache_stat(dst, &st); if( errNum == 0 ) { if(S_ISDIR(st.st_mode)) { error( KIO::ERR_DIR_ALREADY_EXIST, dst.toDisplayString()); return; } if(!(flags & KIO::Overwrite)) { error( KIO::ERR_FILE_ALREADY_EXIST, dst.toDisplayString()); return; } } // Open the source file srcfd = smbc_open(src.toSmbcUrl(), O_RDONLY, 0); if (srcfd < 0){ errNum = errno; } else { errNum = 0; } if(srcfd < 0) { if(errNum == EACCES) { error( KIO::ERR_ACCESS_DENIED, src.toDisplayString() ); } else { error( KIO::ERR_DOES_NOT_EXIST, src.toDisplayString() ); } return; } // Determine initial creation mode if(permissions != -1) { initialmode = permissions | S_IWUSR; } else { initialmode = 0 | S_IWUSR;//0666; } // Open the destination file dstflags = O_CREAT | O_TRUNC | O_WRONLY; if(!(flags & KIO::Overwrite)) { dstflags |= O_EXCL; } dstfd = smbc_open(dst.toSmbcUrl(), dstflags, initialmode); if (dstfd < 0){ errNum = errno; } else { errNum = 0; } if(dstfd < 0) { if(errNum == EACCES) { error(KIO::ERR_WRITE_ACCESS_DENIED, dst.toDisplayString()); } else { error(KIO::ERR_CANNOT_OPEN_FOR_READING, dst.toDisplayString()); } if(srcfd >= 0 ) { smbc_close(srcfd); } return; } // Perform copy while(1) { n = smbc_read(srcfd, buf, MAX_XFER_BUF_SIZE ); if(n > 0) { n = smbc_write(dstfd, buf, n); if(n == -1) { qCDebug(KIO_SMB_LOG) << "SMBSlave::copy copy now KIO::ERR_CANNOT_WRITE"; error( KIO::ERR_CANNOT_WRITE, dst.toDisplayString()); break; } processed_size += n; processedSize(processed_size); } else if(n == 0) { break; // finished } else { error( KIO::ERR_CANNOT_READ, src.toDisplayString()); break; } } // FINISHED: if(srcfd >= 0 ) { smbc_close(srcfd); } if(dstfd >= 0) { if(smbc_close(dstfd) == 0) { // TODO: set final permissions } else { error( KIO::ERR_CANNOT_WRITE, dst.toDisplayString()); return; } } finished(); } void SMBSlave::smbCopyGet(const QUrl& ksrc, const QUrl& kdst, int permissions, KIO::JobFlags flags) { qCDebug(KIO_SMB_LOG) << "src = " << ksrc << ", dest = " << kdst; // check if destination is ok ... const QString dstFile = kdst.toLocalFile(); const QFileInfo dstInfo (dstFile); if(dstInfo.exists()) { if(dstInfo.isDir()) { error (ERR_IS_DIRECTORY, kdst.toDisplayString()); return; } if(!(flags & KIO::Overwrite)) { error(ERR_FILE_ALREADY_EXIST, kdst.toDisplayString()); return; } } bool bResume = false; const QFileInfo partInfo (dstFile + QLatin1String(".part")); const bool bPartExists = partInfo.exists(); const bool bMarkPartial = configValue(QStringLiteral("MarkPartial"), true); if (bMarkPartial && bPartExists && partInfo.size() > 0) { if (partInfo.isDir()) { error(ERR_IS_DIRECTORY, partInfo.absoluteFilePath()); return; } bResume = canResume(partInfo.size()); } if (bPartExists && !bResume) // get rid of an unwanted ".part" file QFile::remove(partInfo.absoluteFilePath()); // open the output file... QFile::OpenMode mode; QString filename; if (bResume) { filename = partInfo.absoluteFilePath(); mode = QFile::WriteOnly | QFile::Append; } else { filename = (bMarkPartial ? partInfo.absoluteFilePath() : dstFile); mode = QFile::WriteOnly | QFile::Truncate; } QFile file (filename); if (!bResume) { QFile::Permissions perms; if (permissions == -1) { perms = QFile::ReadOwner | QFile::WriteOwner; } else { perms = KIO::convertPermissions(permissions | QFile::WriteOwner); } file.setPermissions(perms); } if (!file.open(mode)) { qCDebug(KIO_SMB_LOG) << "could not write to" << dstFile; switch (file.error()) { case QFile::OpenError: if (bResume) { error (ERR_CANNOT_RESUME, kdst.toDisplayString()); } else { error(ERR_CANNOT_OPEN_FOR_WRITING, kdst.toDisplayString()); } break; case QFile::PermissionsError: error(ERR_WRITE_ACCESS_DENIED, kdst.toDisplayString()); break; default: error(ERR_CANNOT_OPEN_FOR_WRITING, kdst.toDisplayString()); break; } return; } // setup the source urls const SMBUrl src(ksrc); // Obtain information about source int errNum = cache_stat (src, &st); if (errNum != 0) { if (errNum == EACCES) { error (KIO::ERR_ACCESS_DENIED, src.toDisplayString()); } else { error (KIO::ERR_DOES_NOT_EXIST, src.toDisplayString()); } return; } if (S_ISDIR( st.st_mode )) { error (KIO::ERR_IS_DIRECTORY, src.toDisplayString()); return; } totalSize(st.st_size); // Open the source file KIO::filesize_t processed_size = 0; int srcfd = smbc_open(src.toSmbcUrl(), O_RDONLY, 0); if (srcfd < 0){ errNum = errno; } else { errNum = 0; if (bResume) { qCDebug(KIO_SMB_LOG) << "seeking to size" << partInfo.size(); off_t offset = smbc_lseek(srcfd, partInfo.size(), SEEK_SET); if (offset == -1) { error(KIO::ERR_CANNOT_SEEK, src.toDisplayString()); smbc_close(srcfd); return; } else { processed_size += offset; } } } if (srcfd < 0) { if(errNum == EACCES) { error( KIO::ERR_ACCESS_DENIED, src.toDisplayString() ); } else { error( KIO::ERR_DOES_NOT_EXIST, src.toDisplayString() ); } return; } // Perform the copy char buf[MAX_XFER_BUF_SIZE]; bool isErr = false; while (1) { const ssize_t bytesRead = smbc_read(srcfd, buf, MAX_XFER_BUF_SIZE); if (bytesRead <= 0) { if (bytesRead < 0) { error( KIO::ERR_CANNOT_READ, src.toDisplayString()); isErr = true; } break; } const qint64 bytesWritten = file.write(buf, bytesRead); if (bytesWritten == -1) { qCDebug(KIO_SMB_LOG) << "copy now KIO::ERR_CANNOT_WRITE"; error( KIO::ERR_CANNOT_WRITE, kdst.toDisplayString()); isErr = true; break; } processed_size += bytesWritten; processedSize(processed_size); } // FINISHED smbc_close(srcfd); // Handle error condition. if (isErr) { const QString sPart = partInfo.absoluteFilePath(); if (bMarkPartial) { const int size = configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); if (partInfo.size() < size) { QFile::remove(sPart); } } return; } // Rename partial file to its original name. if (bMarkPartial) { const QString sPart = partInfo.absoluteFilePath(); // Remove old dest file if it exists.. if (dstInfo.exists()) { QFile::remove(dstFile); } if (!QFile::rename(sPart, dstFile)) { qCDebug(KIO_SMB_LOG) << "failed to rename" << sPart << "to" << dstFile; error(ERR_CANNOT_RENAME_PARTIAL, sPart); return; } } // Restore the mtime on the file. const QString mtimeStr = metaData("modified"); qCDebug(KIO_SMB_LOG) << "modified:" << mtimeStr; if (!mtimeStr.isEmpty()) { QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate); if (dt.isValid()) { struct utimbuf utbuf; - utbuf.actime = QFileInfo(file).lastRead().toSecsSinceEpoch(); // access time, unchanged + utbuf.actime = QFileInfo(dstFile).lastRead().toSecsSinceEpoch(); // access time, unchanged utbuf.modtime = dt.toSecsSinceEpoch(); // modification time utime(QFile::encodeName(dstFile).constData(), &utbuf); } } finished(); } void SMBSlave::smbCopyPut(const QUrl& ksrc, const QUrl& kdst, int permissions, KIO::JobFlags flags) { qCDebug(KIO_SMB_LOG) << "src = " << ksrc << ", dest = " << kdst; QFile srcFile (ksrc.toLocalFile()); const QFileInfo srcInfo (srcFile); if (srcInfo.exists()) { if (srcInfo.isDir()) { error(KIO::ERR_IS_DIRECTORY, ksrc.toDisplayString()); return; } } else { error(KIO::ERR_DOES_NOT_EXIST, ksrc.toDisplayString()); return; } if (!srcFile.open(QFile::ReadOnly)) { qCDebug(KIO_SMB_LOG) << "could not read from" << ksrc; switch (srcFile.error()) { case QFile::PermissionsError: error(KIO::ERR_WRITE_ACCESS_DENIED, ksrc.toDisplayString()); break; case QFile::OpenError: default: error(KIO::ERR_CANNOT_OPEN_FOR_READING, ksrc.toDisplayString()); break; } return; } totalSize(static_cast(srcInfo.size())); bool bResume = false; bool bPartExists = false; const bool bMarkPartial = configValue(QStringLiteral("MarkPartial"), true); const SMBUrl dstOrigUrl (kdst); if (bMarkPartial) { const int errNum = cache_stat(dstOrigUrl.partUrl(), &st); bPartExists = (errNum == 0); if (bPartExists) { if (!(flags & KIO::Overwrite) && !(flags & KIO::Resume)) { bResume = canResume(st.st_size); } else { bResume = (flags & KIO::Resume); } } } int dstfd = -1; int errNum = cache_stat(dstOrigUrl, &st); if (errNum == 0 && !(flags & KIO::Overwrite) && !(flags & KIO::Resume)) { if (S_ISDIR(st.st_mode)) { error( KIO::ERR_IS_DIRECTORY, dstOrigUrl.toDisplayString()); } else { error( KIO::ERR_FILE_ALREADY_EXIST, dstOrigUrl.toDisplayString()); } return; } KIO::filesize_t processed_size = 0; const SMBUrl dstUrl(bMarkPartial ? dstOrigUrl.partUrl() : dstOrigUrl); if (bResume) { // append if resuming qCDebug(KIO_SMB_LOG) << "resume" << dstUrl; dstfd = smbc_open(dstUrl.toSmbcUrl(), O_RDWR, 0 ); if (dstfd < 0) { errNum = errno; } else { const off_t offset = smbc_lseek(dstfd, 0, SEEK_END); if (offset == (off_t)-1) { error(KIO::ERR_CANNOT_SEEK, dstUrl.toDisplayString()); smbc_close(dstfd); return; } else { processed_size = offset; } } } else { mode_t mode; if (permissions == -1) { mode = 600; } else { mode = permissions | S_IRUSR | S_IWUSR; } qCDebug(KIO_SMB_LOG) << "NO resume" << dstUrl; dstfd = smbc_open(dstUrl.toSmbcUrl(), O_CREAT | O_TRUNC | O_WRONLY, mode); if (dstfd < 0) { errNum = errno; } } if (dstfd < 0) { if (errNum == EACCES) { qCDebug(KIO_SMB_LOG) << "access denied"; error( KIO::ERR_WRITE_ACCESS_DENIED, dstUrl.toDisplayString()); } else { qCDebug(KIO_SMB_LOG) << "can not open for writing"; error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, dstUrl.toDisplayString()); } return; } bool isErr = false; if (processed_size == 0 || srcFile.seek(processed_size)) { // Perform the copy char buf[MAX_XFER_BUF_SIZE]; while (1) { const ssize_t bytesRead = srcFile.read(buf, MAX_XFER_BUF_SIZE); if (bytesRead <= 0) { if (bytesRead < 0) { error(KIO::ERR_CANNOT_READ, ksrc.toDisplayString()); isErr = true; } break; } const qint64 bytesWritten = smbc_write(dstfd, buf, bytesRead); if (bytesWritten == -1) { error(KIO::ERR_CANNOT_WRITE, kdst.toDisplayString()); isErr = true; break; } processed_size += bytesWritten; processedSize(processed_size); } } else { isErr = true; error(KIO::ERR_CANNOT_SEEK, ksrc.toDisplayString()); } // FINISHED if (smbc_close(dstfd) < 0) { qCDebug(KIO_SMB_LOG) << dstUrl << "could not write"; error( KIO::ERR_CANNOT_WRITE, dstUrl.toDisplayString()); return; } // Handle error condition. if (isErr) { if (bMarkPartial) { const int size = configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); const int errNum = cache_stat(dstUrl, &st); if (errNum == 0 && st.st_size < size) { smbc_unlink(dstUrl.toSmbcUrl()); } } return; } // Rename partial file to its original name. if (bMarkPartial) { smbc_unlink(dstOrigUrl.toSmbcUrl()); if (smbc_rename(dstUrl.toSmbcUrl(), dstOrigUrl.toSmbcUrl()) < 0) { qCDebug(KIO_SMB_LOG) << "failed to rename" << dstUrl << "to" << dstOrigUrl << "->" << strerror(errno); error(ERR_CANNOT_RENAME_PARTIAL, dstUrl.toDisplayString()); return; } } #ifdef HAVE_UTIME_H // set modification time const QString mtimeStr = metaData( "modified" ); if (!mtimeStr.isEmpty() ) { QDateTime dt = QDateTime::fromString( mtimeStr, Qt::ISODate ); if ( dt.isValid() ) { struct utimbuf utbuf; utbuf.actime = st.st_atime; // access time, unchanged utbuf.modtime = dt.toSecsSinceEpoch(); // modification time - smbc_utime( dstUrl.toSmbcUrl(), &utbuf ); + smbc_utime( dstOrigUrl.toSmbcUrl(), &utbuf ); } } #endif // We have done our job => finish finished(); } //=========================================================================== void SMBSlave::del( const QUrl &kurl, bool isfile) { qCDebug(KIO_SMB_LOG) << kurl; m_current_url = kurl; int errNum = 0; int retVal = 0; if(isfile) { // Delete file qCDebug(KIO_SMB_LOG) << "Deleting file" << kurl; retVal = smbc_unlink(m_current_url.toSmbcUrl()); if ( retVal < 0 ){ errNum = errno; } else { errNum = 0; } } else { qCDebug(KIO_SMB_LOG) << "Deleting directory" << kurl; // Delete directory retVal = smbc_rmdir(m_current_url.toSmbcUrl()); if( retVal < 0 ) { errNum = errno; } else { errNum = 0; } } if( errNum != 0 ) { reportError(kurl, errNum); } else { finished(); } } //=========================================================================== void SMBSlave::mkdir( const QUrl &kurl, int permissions ) { qCDebug(KIO_SMB_LOG) << kurl; int errNum = 0; int retVal = 0; m_current_url = kurl; retVal = smbc_mkdir(m_current_url.toSmbcUrl(), 0777); if( retVal < 0 ){ errNum = errno; } else { errNum = 0; } if( retVal < 0 ) { if (errNum == EEXIST) { errNum = cache_stat(m_current_url, &st ); if (errNum == 0 && S_ISDIR(st.st_mode)) { error( KIO::ERR_DIR_ALREADY_EXIST, m_current_url.toDisplayString()); } else { error( KIO::ERR_FILE_ALREADY_EXIST, m_current_url.toDisplayString()); } } else { reportError(kurl, errNum); } qCDebug(KIO_SMB_LOG) << "exit with error " << kurl; } else // success { if(permissions != -1) { // TODO enable the following when complete //smbc_chmod( url.toSmbcUrl(), permissions ); } finished(); } } //=========================================================================== void SMBSlave::rename( const QUrl& ksrc, const QUrl& kdest, KIO::JobFlags flags ) { SMBUrl src; SMBUrl dst; int errNum = 0; int retVal = 0; qCDebug(KIO_SMB_LOG) << "old name = " << ksrc << ", new name = " << kdest; src = ksrc; dst = kdest; // Check to se if the destination exists qCDebug(KIO_SMB_LOG) << "stat dst"; errNum = cache_stat(dst, &st); if( errNum == 0 ) { if(S_ISDIR(st.st_mode)) { qCDebug(KIO_SMB_LOG) << "KIO::ERR_DIR_ALREADY_EXIST"; error( KIO::ERR_DIR_ALREADY_EXIST, dst.toDisplayString()); return; } if(!(flags & KIO::Overwrite)) { qCDebug(KIO_SMB_LOG) << "KIO::ERR_FILE_ALREADY_EXIST"; error( KIO::ERR_FILE_ALREADY_EXIST, dst.toDisplayString()); return; } } qCDebug(KIO_SMB_LOG) << "smbc_rename " << src.toSmbcUrl() << " " << dst.toSmbcUrl(); retVal = smbc_rename(src.toSmbcUrl(), dst.toSmbcUrl()); if( retVal < 0 ){ errNum = errno; } else { errNum = 0; } if( retVal < 0 ) { qCDebug(KIO_SMB_LOG) << "failed "; switch(errNum) { case ENOENT: errNum = cache_stat(src, &st); if( errNum != 0 ) { if(errNum == EACCES) { qCDebug(KIO_SMB_LOG) << "KIO::ERR_ACCESS_DENIED"; error(KIO::ERR_ACCESS_DENIED, src.toDisplayString()); } else { qCDebug(KIO_SMB_LOG) << "KIO::ERR_DOES_NOT_EXIST"; error(KIO::ERR_DOES_NOT_EXIST, src.toDisplayString()); } } break; case EACCES: case EPERM: qCDebug(KIO_SMB_LOG) << "KIO::ERR_ACCESS_DENIED"; error( KIO::ERR_ACCESS_DENIED, dst.toDisplayString() ); break; default: qCDebug(KIO_SMB_LOG) << "KIO::ERR_CANNOT_RENAME"; error( KIO::ERR_CANNOT_RENAME, src.toDisplayString() ); } qCDebug(KIO_SMB_LOG) << "exit with error"; return; } qCDebug(KIO_SMB_LOG) << "everything fine\n"; finished(); } diff --git a/smb/kio_smb_internal.cpp b/smb/kio_smb_internal.cpp index 872cae73..b2cc6ff1 100644 --- a/smb/kio_smb_internal.cpp +++ b/smb/kio_smb_internal.cpp @@ -1,150 +1,158 @@ ///////////////////////////////////////////////////////////////////////////// // // Project: SMB kioslave for KDE2 // // File: kio_smb_internal.cpp // // Abstract: Utility class implementation used by SMBSlave // // Author(s): Matthew Peterson // //--------------------------------------------------------------------------- // // Copyright (c) 2000 Caldera Systems, Inc. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2.1 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; see the file COPYING. If not, please obtain // a copy from https://www.gnu.org/copyleft/gpl.html // ///////////////////////////////////////////////////////////////////////////// #include "kio_smb_internal.h" #include "kio_smb.h" #include #include // for QDir::cleanPath //=========================================================================== // SMBUrl Function Implementation //=========================================================================== //----------------------------------------------------------------------- SMBUrl::SMBUrl() { m_type = SMBURLTYPE_UNKNOWN; } //----------------------------------------------------------------------- SMBUrl::SMBUrl(const QUrl& kurl) : QUrl(kurl) //----------------------------------------------------------------------- { + // We treat cifs as an alias but need to translate it to smb. + // https://bugs.kde.org/show_bug.cgi?id=327295 + // It's not IANA registered and also libsmbc internally expects + // smb URIs so we do very broadly coerce cifs to smb. + // Also see SMBSlave::checkURL. + if (scheme() == "cifs") { + setScheme("smb"); + } updateCache(); } SMBUrl::SMBUrl(const SMBUrl& other) : QUrl(other), m_surl(other.m_surl), m_type(other.m_type) { } SMBUrl& SMBUrl::operator=(const SMBUrl&) = default; //----------------------------------------------------------------------- void SMBUrl::addPath(const QString &filedir) { if(path().length() > 0 && path().at(path().length() - 1) != QLatin1Char('/')) { QUrl::setPath(path() + QLatin1Char('/') + filedir); } else { QUrl::setPath(path() + filedir); } updateCache(); } //----------------------------------------------------------------------- bool SMBUrl::cd(const QString &filedir) { if (filedir == "..") { setUrl(KIO::upUrl(*this).url()); } else { setUrl(filedir); } updateCache(); return true; } //----------------------------------------------------------------------- void SMBUrl::updateCache() //----------------------------------------------------------------------- { QUrl::setPath(QDir::cleanPath(path())); // SMB URLs are UTF-8 encoded qCDebug(KIO_SMB_LOG) << "updateCache " << QUrl::path(); if ( QUrl::url() == "smb:/" ) m_surl = "smb://"; else m_surl = toString(QUrl::PrettyDecoded).toUtf8(); m_type = SMBURLTYPE_UNKNOWN; // update m_type (void)getType(); } //----------------------------------------------------------------------- SMBUrlType SMBUrl::getType() const // Returns the type of this SMBUrl: // SMBURLTYPE_UNKNOWN - Type could not be determined. Bad SMB Url. // SMBURLTYPE_ENTIRE_NETWORK - "smb:/" is entire network // SMBURLTYPE_WORKGROUP_OR_SERVER - "smb:/mygroup" or "smb:/myserver" // SMBURLTYPE_SHARE_OR_PATH - "smb:/mygroupe/mymachine/myshare/mydir" //----------------------------------------------------------------------- { if(m_type != SMBURLTYPE_UNKNOWN) return m_type; if (scheme() != "smb") { m_type = SMBURLTYPE_UNKNOWN; return m_type; } if (path().isEmpty() || path(QUrl::FullyDecoded) == "/") { if (host().isEmpty()) m_type = SMBURLTYPE_ENTIRE_NETWORK; else m_type = SMBURLTYPE_WORKGROUP_OR_SERVER; return m_type; } // Check for the path if we get this far m_type = SMBURLTYPE_SHARE_OR_PATH; return m_type; } SMBUrl SMBUrl::partUrl() const { if (m_type == SMBURLTYPE_SHARE_OR_PATH && !fileName().isEmpty()) { SMBUrl url (*this); url.setPath(path() + QLatin1String(".part")); return url; } return SMBUrl(); } diff --git a/smb/smb.json b/smb/smb.json index 99fc84c2..d66629bf 100644 --- a/smb/smb.json +++ b/smb/smb.json @@ -1,34 +1,64 @@ { "KDE-KIO-Protocols": { "smb": { "Class": ":internet", "Icon": "network-workgroup", "X-DocPath": "kioslave5/smb/index.html", "copyFromFile": true, "copyToFile": true, "deleting": true, "exec": "kf5/kio/smb", "input": "none", "listing": [ "Name", "Type", "Size", "Date", "Access", "Owner", "Group" ], "linking": false, "makedir": true, "maxInstances": 20, "maxInstancesPerHost": 5, "moving": true, "output": "filesystem", "protocol": "smb", "opening": true, "truncating": true, "reading": true, "writing": true + }, + "cifs": { + "Class": ":internet", + "Icon": "network-workgroup", + "X-DocPath": "kioslave5/smb/index.html", + "copyFromFile": true, + "copyToFile": true, + "deleting": true, + "exec": "kf5/kio/smb", + "input": "none", + "listing": [ + "Name", + "Type", + "Size", + "Date", + "Access", + "Owner", + "Group" + ], + "linking": false, + "makedir": true, + "maxInstances": 20, + "maxInstancesPerHost": 5, + "moving": true, + "output": "filesystem", + "protocol": "cifs", + "opening": true, + "truncating": true, + "reading": true, + "writing": true } } } diff --git a/thumbnail/appimagethumbnail.desktop b/thumbnail/appimagethumbnail.desktop index 57cddd3d..207cdec9 100644 --- a/thumbnail/appimagethumbnail.desktop +++ b/thumbnail/appimagethumbnail.desktop @@ -1,34 +1,35 @@ [Desktop Entry] Type=Service Name=AppImage Name[ca]=AppImage Name[ca@valencia]=AppImage Name[cs]=AppImage Name[da]=AppImage Name[de]=AppImage Name[el]=AppImage Name[en_GB]=AppImage Name[es]=AppImage Name[et]=AppImage Name[eu]=AppIrudia Name[fi]=AppImage Name[fr]=AppImage Name[gl]=AppImage +Name[ia]=AppImage Name[it]=AppImage Name[ko]=AppImage Name[lt]=AppImage failai Name[nl]=AppImage Name[nn]=AppImage Name[pl]=AppImage Name[pt]=AppImage Name[pt_BR]=AppImage Name[sk]=AppImage Name[sv]=AppImage Name[uk]=AppImage Name[x-test]=xxAppImagexx Name[zh_CN]=AppImage Name[zh_TW]=AppImage X-KDE-ServiceTypes=ThumbCreator MimeType=application/x-iso9660-appimage;application/vnd.appimage; X-KDE-Library=appimagethumbnail CacheThumbnail=true diff --git a/thumbnail/ebookthumbnail.desktop b/thumbnail/ebookthumbnail.desktop index a557a84a..c05cfb7d 100644 --- a/thumbnail/ebookthumbnail.desktop +++ b/thumbnail/ebookthumbnail.desktop @@ -1,36 +1,37 @@ [Desktop Entry] Type=Service Name=eBooks Name[ca]=eBooks Name[ca@valencia]=eBooks Name[cs]=eKnihy Name[da]=E-bøger Name[de]=eBooks Name[el]=eBooks Name[en_GB]=eBooks Name[es]=eBooks Name[et]=E-raamatud Name[eu]=eLiburukiak Name[fi]=eBooks Name[fr]=Livres électroniques Name[gl]=Libros electrónicos +Name[ia]=eLibros (eBooks) Name[it]=eBook Name[ko]=전자책 Name[lt]=El. knygos Name[nl]=eBooks Name[nn]=E-bøker Name[pl]=eBooki Name[pt]=eBooks Name[pt_BR]=eBooks Name[sk]=eKnihy Name[sv]=e-böcker Name[uk]=Електронні книги Name[x-test]=xxeBooksxx Name[zh_CN]=电子书 Name[zh_TW]=eBooks X-KDE-ServiceTypes=ThumbCreator MimeType=application/epub+zip;application/x-fictionbook+xml;application/x-zip-compressed-fb2; X-KDE-Library=ebookthumbnail CacheThumbnail=true diff --git a/thumbnail/opendocumentthumbnail.desktop b/thumbnail/opendocumentthumbnail.desktop index 98fb5986..594440e1 100644 --- a/thumbnail/opendocumentthumbnail.desktop +++ b/thumbnail/opendocumentthumbnail.desktop @@ -1,37 +1,38 @@ [Desktop Entry] Type=Service Name=Office Documents Name[ca]=Documents de l'Office Name[ca@valencia]=Documents de l'Office Name[cs]=Kancelářské dokumenty Name[da]=Office-dokumenter Name[de]=Office-Dokumente Name[el]=Έγγραφα Office Name[en_GB]=Office Documents Name[es]=Documentos de Office Name[et]=Kontoritöö rakenduste dokumendid Name[eu]=Bulegotika dokumentuak Name[fi]=Toimistotiedostot Name[fr]=Documents Office Name[gl]=Documentos de oficina +Name[ia]=Documentos de Officio (Office) Name[it]=Documenti d'ufficio Name[ja]=Office ドキュメント Name[ko]=오피스 문서 Name[lt]=Raštinės dokumentai Name[nl]=Kantoordocumenten Name[nn]=Kontordokument Name[pl]=Dokumenty biurowe Name[pt]=Documentos de Escritório Name[pt_BR]=Documentos de escritório Name[sk]=Kancelárske dokumenty Name[sv]=Kontorsdokument Name[uk]=Офісні документи Name[x-test]=xxOffice Documentsxx Name[zh_CN]=Office 文档 Name[zh_TW]=Office 文件 X-KDE-ServiceTypes=ThumbCreator MimeType=application/vnd.oasis.opendocument.text;application/vnd.oasis.opendocument.text-template;application/vnd.oasis.opendocument.text-master;application/vnd.oasis.opendocument.text-master-template;application/vnd.oasis.opendocument.spreadsheet;application/vnd.oasis.opendocument.spreadsheet-template;application/vnd.oasis.opendocument.graphics;application/vnd.oasis.opendocument.graphics-template;application/vnd.oasis.opendocument.presentation;application/vnd.oasis.opendocument.presentation-template;application/vnd.oasis.opendocument.formula;application/vnd.oasis.opendocument.formula-template;application/vnd.oasis.opendocument.chart;application/vnd.oasis.opendocument.chart-template;application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/vnd.openxmlformats-officedocument.wordprocessingml.template;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;application/vnd.openxmlformats-officedocument.spreadsheetml.template;application/vnd.openxmlformats-officedocument.presentationml.presentation;application/vnd.openxmlformats-officedocument.presentationml.template;application/oxps;application/vnd.ms-xpsdocument; X-KDE-Library=opendocumentthumbnail CacheThumbnail=true