diff --git a/smb/dnssddiscoverer.cpp b/smb/dnssddiscoverer.cpp index cb694f42..3a8f19a1 100644 --- a/smb/dnssddiscoverer.cpp +++ b/smb/dnssddiscoverer.cpp @@ -1,118 +1,119 @@ /* Copyright 2019 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 3 of the License or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "dnssddiscoverer.h" #include "kio_smb.h" DNSSDDiscovery::DNSSDDiscovery(KDNSSD::RemoteService::Ptr service) : m_service(service) { } KIO::UDSEntry DNSSDDiscovery::toEntry() const { KIO::UDSEntry entry; entry.fastInsert(KIO::UDSEntry::UDS_NAME, m_service->serviceName()); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, "network-server"); // 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; u.setScheme(QStringLiteral("smb")); u.setHost(m_service->hostName()); - if (m_service->port() > 0 && m_service->port() != 445 /* default smb */) { + const int defaultPort = 445; + if (m_service->port() > 0 && m_service->port() != defaultPort) { u.setPort(m_service->port()); } u.setPath("/"); // https://bugs.kde.org/show_bug.cgi?id=388922 entry.fastInsert(KIO::UDSEntry::UDS_URL, u.url()); entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("application/x-smb-server")); return entry; } DNSSDDiscoverer::DNSSDDiscoverer() { connect(&m_browser, &KDNSSD::ServiceBrowser::serviceAdded, this, [=](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 &servicePtr : qAsConst(m_services)) { if (*service == *servicePtr) { return; } } connect(service.data(), &KDNSSD::RemoteService::resolved, this, [=] { ++m_resolvedCount; emit newDiscovery(Discovery::Ptr(new DNSSDDiscovery(service))); maybeFinish(); }); // Schedule resolution of hostname. We'll later call resolve // which will block until the resolution is done. This basically // gives us a head start on discovery. service->resolveAsync(); m_services.append(service); }); connect(&m_browser, &KDNSSD::ServiceBrowser::finished, this, &DNSSDDiscoverer::stop); } void DNSSDDiscoverer::start() { m_browser.startBrowse(); } void DNSSDDiscoverer::stop() { m_browser.disconnect(); m_disconnected = true; maybeFinish(); } bool DNSSDDiscoverer::isFinished() const { return m_disconnected && m_services.count() == m_resolvedCount; } void DNSSDDiscoverer::maybeFinish() { if (isFinished()) { emit finished(); } } diff --git a/smb/kio_smb.cpp b/smb/kio_smb.cpp index 4e0bc7d2..e4a12565 100644 --- a/smb/kio_smb.cpp +++ b/smb/kio_smb.cpp @@ -1,122 +1,120 @@ ///////////////////////////////////////////////////////////////////////////// // // Project: SMB kioslave for KDE // // File: Top level implementation file for kio_smb.cpp // // Abstract: member function implementations for 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.h" #include "kio_smb_internal.h" #include #include // Pseudo plugin class to embed meta data class KIOPluginForMetaData : public QObject { Q_OBJECT Q_PLUGIN_METADATA(IID "org.kde.kio.slave.smb" FILE "smb.json") }; bool needsEEXISTWorkaround() { /* There is an issue with some libsmbclient versions that return EEXIST * return code from smbc_opendir() instead of EPERM when the user * tries to access a resource that requires login authetification. * We are working around the issue by treating EEXIST as a special case * of "invalid/unavailable credentials" if we detect that we are using * the affected versions of libsmbclient * * Upstream bug report: https://bugzilla.samba.org/show_bug.cgi?id=13050 */ static const QVersionNumber firstBrokenVer{4, 7, 0}; static const QVersionNumber lastBrokenVer{4, 7, 6}; const QVersionNumber currentVer = QVersionNumber::fromString(smbc_version()); qCDebug(KIO_SMB_LOG) << "Using libsmbclient library version" << currentVer; if (currentVer >= firstBrokenVer && currentVer <= lastBrokenVer) { qCDebug(KIO_SMB_LOG) << "Detected broken libsmbclient version" << currentVer; return true; } return false; } //=========================================================================== SMBSlave::SMBSlave(const QByteArray& pool, const QByteArray& app) : SlaveBase( "smb", pool, app ), m_openFd(-1), m_enableEEXISTWorkaround(needsEEXISTWorkaround()) { m_initialized_smbc = false; //read in the default workgroup info... reparseConfiguration(); //initialize the library... auth_initialize_smbc(); } //=========================================================================== -SMBSlave::~SMBSlave() -{ -} +SMBSlave::~SMBSlave() = default; void SMBSlave::virtual_hook(int id, void *data) { switch(id) { case SlaveBase::GetFileSystemFreeSpace: { QUrl *url = static_cast(data); fileSystemFreeSpace(*url); } break; case SlaveBase::Truncate: { auto length = static_cast(data); truncate(*length); } break; default: { SlaveBase::virtual_hook(id, data); } break; } } //=========================================================================== int Q_DECL_EXPORT kdemain( int argc, char **argv ) { QCoreApplication app(argc, argv); if( argc != 4 ) { qCDebug(KIO_SMB_LOG) << "Usage: kio_smb protocol domain-socket1 domain-socket2"; return -1; } SMBSlave slave( argv[2], argv[3] ); slave.dispatchLoop(); return 0; } #include "kio_smb.moc" diff --git a/smb/kio_smb.h b/smb/kio_smb.h index 079d9947..ba4425d1 100644 --- a/smb/kio_smb.h +++ b/smb/kio_smb.h @@ -1,322 +1,322 @@ ///////////////////////////////////////////////////////////////////////////// // // Project: SMB kioslave for KDE // // File: kio_smb.h // // Abstract: The main kio slave class declaration. For convenience, // in concurrent development, the implementation for this class // is separated into several .cpp files -- the file containing // the implementation should be noted in the comments for each // member function. // // 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 // ///////////////////////////////////////////////////////////////////////////// #ifndef KIO_SMB_H_INCLUDED #define KIO_SMB_H_INCLUDED #include #include "smb-logsettings.h" //-------------- // KDE includes //-------------- #include #include //----------------------------- // Standard C library includes //----------------------------- #include #include #include #include #include #include #include #include #include #include //----------------------------- // Qt includes //----------------------------- #include #include #include //------------------------------- // Samba client library includes //------------------------------- extern "C" { #include } //--------------------------- // kio_smb internal includes //--------------------------- #include "kio_smb_internal.h" #define MAX_XFER_BUF_SIZE 65534 using namespace KIO; //=========================================================================== class SMBSlave : public QObject, public KIO::SlaveBase { Q_OBJECT private: class SMBError { public: int kioErrorId; QString errorString; }; //--------------------------------------------------------------------- // please make sure your private data does not duplicate existing data //--------------------------------------------------------------------- bool m_initialized_smbc; /** * From Controlcenter */ QString m_default_user; QString m_default_workgroup = QStringLiteral("WORKGROUP"); // overwritten with value from smbc QString m_default_password; QString m_default_encoding; /** * we store the current url, it's needed for * callback authorization method */ SMBUrl m_current_url; /** * From Controlcenter, show SHARE$ or not */ // bool m_showHiddenShares; //currently unused, Alex /** * libsmbclient need global variables to store in, * else it crashes on exit next method after use cache_stat, * looks like gcc (C/C++) failure */ - struct stat st; + struct stat st{}; protected: //--------------------------------------------- // Authentication functions (kio_smb_auth.cpp) //--------------------------------------------- // (please prefix functions with auth) /** * Description : Initializes the libsmbclient * Return : true on success false with errno set on error */ bool auth_initialize_smbc(); int checkPassword(SMBUrl &url); //--------------------------------------------- // Cache functions (kio_smb_auth.cpp) //--------------------------------------------- //Stat methods //----------------------------------------- // Browsing functions (kio_smb_browse.cpp) //----------------------------------------- // (please prefix functions with browse) /** * Description : Return a stat of given SMBUrl. Calls cache_stat and * pack it in UDSEntry. UDSEntry will not be cleared * Parameter : SMBUrl the url to stat * Return : cache_stat() return code */ int browse_stat_path(const SMBUrl& url, UDSEntry& udsentry); /** * Description : call smbc_stat and return stats of the url * Parameter : SMBUrl the url to stat * Return : stat* of the url * Note : it has some problems with stat in method, looks like * something leave(or removed) on the stack. If your * method segfault on returning try to change the stat* * variable */ int cache_stat( const SMBUrl& url, struct stat* st ); //--------------------------------------------- // Configuration functions (kio_smb_config.cpp) //--------------------------------------------- // (please prefix functions with config) //--------------------------------------- // Directory functions (kio_smb_dir.cpp) //--------------------------------------- // (please prefix functions with dir) //-------------------------------------- // File IO functions (kio_smb_file.cpp) //-------------------------------------- // (please prefix functions with file) //---------------------------- // Misc functions (this file) //---------------------------- /** * Description : correct a given URL * valid URL's are * * smb://[[domain;]user[:password]@]server[:port][/share[/path[/file]]] * smb:/[[domain;]user[:password]@][group/[server[/share[/path[/file]]]]] * domain = workgroup(domain) of the user * user = username * password = password of useraccount * group = workgroup(domain) of server * server = host to connect * share = a share of the server (host) * path = a path of the share * Parameter : QUrl the url to check * Return : new QUrl if it is corrected. else the same QUrl */ QUrl checkURL(const QUrl& kurl) const; void reportError(const SMBUrl& url, const int errNum); void reportWarning(const SMBUrl& url, const int errNum); public: //----------------------------------------------------------------------- // smbclient authentication callback (note that this is called by the // global ::auth_smbc_get_data() call. void auth_smbc_get_data(const char *server,const char *share, char *workgroup, int wgmaxlen, char *username, int unmaxlen, char *password, int pwmaxlen); //----------------------------------------------------------------------- // Overwritten functions from the base class that define the operation of // this slave. (See the base class headerfile slavebase.h for more // details) //----------------------------------------------------------------------- // Functions overwritten in kio_smb.cpp SMBSlave(const QByteArray& pool, const QByteArray& app); ~SMBSlave() override; // Functions overwritten in kio_smb_browse.cpp void listDir( const QUrl& url ) override; void stat( const QUrl& url ) override; // Functions overwritten in kio_smb_config.cpp void reparseConfiguration() override; // Functions overwritten in kio_smb_dir.cpp - void copy( const QUrl& src, const QUrl &dest, int permissions, KIO::JobFlags flags ) override; + void copy( const QUrl& src, const QUrl &dst, int permissions, KIO::JobFlags flags ) override; void del( const QUrl& kurl, bool isfile) override; void mkdir( const QUrl& kurl, int permissions ) override; void rename( const QUrl& src, const QUrl& dest, KIO::JobFlags flags ) override; // Functions overwritten in kio_smb_file.cpp void get( const QUrl& kurl ) override; void put( const QUrl& kurl, int permissions, KIO::JobFlags flags ) override; void open( const QUrl& kurl, QIODevice::OpenMode mode ) override; void read( KIO::filesize_t bytesRequested ) override; void write( const QByteArray &fileData ) override; void seek( KIO::filesize_t offset ) override; void truncate( KIO::filesize_t length ); void close() override; // Functions not implemented (yet) //virtual void setHost(const QString& host, int port, const QString& user, const QString& pass); //virtual void openConnection(); //virtual void closeConnection(); //virtual void slave_status(); void special( const QByteArray & ) override; protected: void virtual_hook(int id, void *data) override; private: SMBError errnumToKioError(const SMBUrl& url, const int errNum); - void smbCopy(const QUrl& src, const QUrl &dest, int permissions, KIO::JobFlags flags); - void smbCopyGet(const QUrl& src, const QUrl& dest, int permissions, KIO::JobFlags flags); - void smbCopyPut(const QUrl& src, const QUrl& dest, int permissions, KIO::JobFlags flags); + void smbCopy(const QUrl& src, const QUrl &dst, int permissions, KIO::JobFlags flags); + void smbCopyGet(const QUrl& ksrc, const QUrl& kdst, int permissions, KIO::JobFlags flags); + void smbCopyPut(const QUrl& ksrc, const QUrl& kdst, int permissions, KIO::JobFlags flags); bool workaroundEEXIST(const int errNum) const; void fileSystemFreeSpace(const QUrl &url); /** * Used in open(), read(), write(), and close() * FIXME Placing these in the private section above causes m_openUrl = kurl * to fail in SMBSlave::open. Need to find out why this is. */ int m_openFd; SMBUrl m_openUrl; const bool m_enableEEXISTWorkaround; /* Enables a workaround for some broken libsmbclient versions */ // Close without calling finish(). Use this to close after error. void closeWithoutFinish(); }; //========================================================================== // the global libsmbclient authentication callback function extern "C" { void auth_smbc_get_data(SMBCCTX * context, const char *server,const char *share, char *workgroup, int wgmaxlen, char *username, int unmaxlen, char *password, int pwmaxlen); } //=========================================================================== // Main slave entrypoint (see kio_smb.cpp) extern "C" { int kdemain( int argc, char **argv ); } #endif //#endif KIO_SMB_H_INCLUDED diff --git a/smb/kio_smb_auth.cpp b/smb/kio_smb_auth.cpp index 6fffce99..fe8585ae 100644 --- a/smb/kio_smb_auth.cpp +++ b/smb/kio_smb_auth.cpp @@ -1,249 +1,249 @@ ///////////////////////////////////////////////////////////////////////////// // // Project: SMB kioslave for KDE2 // // File: kio_smb_auth.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 // call for libsmbclient //========================================================================== void auth_smbc_get_data(SMBCCTX * context, const char *server,const char *share, char *workgroup, int wgmaxlen, char *username, int unmaxlen, char *password, int pwmaxlen) //========================================================================== { if (context != nullptr) { #ifdef DEPRECATED_SMBC_INTERFACE - SMBSlave *theSlave = (SMBSlave*) smbc_getOptionUserData(context); + auto *theSlave = static_cast(smbc_getOptionUserData(context)); #else - SMBSlave *theSlave = (SMBSlave*)smbc_option_get(context, "user_data"); + auto *theSlave = static_cast(smbc_option_get(context, "user_data")); #endif theSlave->auth_smbc_get_data(server, share, workgroup,wgmaxlen, username, unmaxlen, password, pwmaxlen); } } //-------------------------------------------------------------------------- void SMBSlave::auth_smbc_get_data(const char *server,const char *share, char *workgroup, int wgmaxlen, char *username, int unmaxlen, char *password, int pwmaxlen) //-------------------------------------------------------------------------- { //check this to see if we "really" need to authenticate... SMBUrlType t = m_current_url.getType(); if( t == SMBURLTYPE_ENTIRE_NETWORK ) { qCDebug(KIO_SMB_LOG) << "we don't really need to authenticate for this top level url, returning"; return; } qCDebug(KIO_SMB_LOG) << "auth_smbc_get_dat: set user=" << username << ", workgroup=" << workgroup << " server=" << server << ", share=" << share; QString s_server = QString::fromUtf8(server); QString s_share = QString::fromUtf8(share); workgroup[wgmaxlen - 1] = 0; QString s_workgroup = QString::fromUtf8(workgroup); username[unmaxlen - 1] = 0; QString s_username = QString::fromUtf8(username); password[pwmaxlen - 1] = 0; QString s_password = QString::fromUtf8(password); KIO::AuthInfo info; info.url = QUrl("smb:///"); info.url.setHost(s_server); info.url.setPath('/' + s_share); info.username = s_username; info.password = s_password; info.verifyPath = true; info.setExtraField("domain", s_workgroup); qCDebug(KIO_SMB_LOG) << "libsmb-auth-callback URL:" << info.url; if ( !checkCachedAuthentication( info ) ) { if ( m_default_user.isEmpty() ) { // ok, we do not know the password. Let's try anonymous before we try for real info.username = "anonymous"; info.password.clear(); } else { // user defined a default username/password in kcontrol; try this info.username = m_default_user; info.password = m_default_password; } } else qCDebug(KIO_SMB_LOG) << "got password through cache"; // Make sure it'll be safe to cast to size_t (unsigned) Q_ASSERT(unmaxlen > 0); Q_ASSERT(pwmaxlen > 0); Q_ASSERT(wgmaxlen > 0); strncpy(username, info.username.toUtf8(), static_cast(unmaxlen - 1)); strncpy(password, info.password.toUtf8(), static_cast(pwmaxlen - 1)); // TODO: isEmpty guard can be removed in 20.08+ // It is only here to prevent us setting an empty work group if a user updates // but doesn't restart so kiod5 could hold an old cache without domain // field. In that event we'll leave the input workgroup as-is. const QString domain = info.getExtraField("domain").toString(); if (!domain.isEmpty()) { strncpy(workgroup, domain.toUtf8(), static_cast(wgmaxlen - 1)); } } int SMBSlave::checkPassword(SMBUrl &url) { qCDebug(KIO_SMB_LOG) << "checkPassword for " << url; KIO::AuthInfo info; info.url = QUrl("smb:///"); info.url.setHost(url.host()); QString share = url.path(); int index = share.indexOf('/', 1); if (index > 1) share = share.left(index); if (share.at(0) == '/') share = share.mid(1); info.url.setPath('/' + share); info.verifyPath = true; info.keepPassword = true; info.setExtraField("anonymous", true); // arbitrary default for dialog info.setExtraField("domain", m_default_workgroup); if ( share.isEmpty() ) info.prompt = i18n( "Please enter authentication information for %1" , url.host() ); else info.prompt = i18n( "Please enter authentication information for:\n" "Server = %1\n" "Share = %2" , url.host() , share ); info.username = url.userName(); qCDebug(KIO_SMB_LOG) << "call openPasswordDialog for " << info.url; const int passwordDialogErrorCode = openPasswordDialogV2(info); if (passwordDialogErrorCode == KJob::NoError) { qCDebug(KIO_SMB_LOG) << "openPasswordDialog returned " << info.username; url.setUser(info.username); if (info.keepPassword) { qCDebug(KIO_SMB_LOG) << "Caching info.username = " << info.username << ", info.url = " << info.url.toDisplayString(); cacheAuthentication(info); } return KJob::NoError; } qCDebug(KIO_SMB_LOG) << "no value from openPasswordDialog; error:" << passwordDialogErrorCode; return passwordDialogErrorCode; } //-------------------------------------------------------------------------- // Initializes the smbclient library // // Returns: 0 on success -1 with errno set on error bool SMBSlave::auth_initialize_smbc() { if (m_initialized_smbc) { return true; } qCDebug(KIO_SMB_LOG) << "auth_initialize_smbc"; KConfig cfg("kioslaverc", KConfig::SimpleConfig); int debug_level = cfg.group("SMB").readEntry("DebugLevel", 0); qCDebug(KIO_SMB_LOG) << "smbc_new_context call"; SMBCCTX *smb_context = smbc_new_context(); if (!smb_context) { SlaveBase::error(ERR_INTERNAL, i18n("libsmbclient failed to create context")); return false; } #ifdef DEPRECATED_SMBC_INTERFACE // defined by libsmbclient.h of Samba 3.2 /* New libsmbclient interface of Samba 3.2 */ smbc_setDebug(smb_context, debug_level); smbc_setFunctionAuthDataWithContext(smb_context, ::auth_smbc_get_data); smbc_setOptionUserData(smb_context, this); /* Enable Kerberos support */ smbc_setOptionUseKerberos(smb_context, 1); smbc_setOptionFallbackAfterKerberos(smb_context, 1); #else smb_context->debug = debug_level; smb_context->callbacks.auth_fn = NULL; smbc_option_set(smb_context, "auth_function", (void*)::auth_smbc_get_data); smbc_option_set(smb_context, "user_data", this); #if defined(SMB_CTX_FLAG_USE_KERBEROS) && defined(SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) smb_context->flags |= SMB_CTX_FLAG_USE_KERBEROS | SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS; #endif #endif /* DEPRECATED_SMBC_INTERFACE */ if (!smbc_init_context(smb_context)) { smbc_free_context(smb_context, 0); smb_context = nullptr; SlaveBase::error(ERR_INTERNAL, i18n("libsmbclient failed to initialize context")); return false; } smbc_set_context(smb_context); // TODO: refactor; checkPassword should query this on // demand to not run into situations where we may have cached // the workgroup early on and it changed since. Needs context // being held in the slave though, which opens us up to nullptr // problems should checkPassword be called without init first. m_default_workgroup = smbc_getWorkgroup(smb_context); m_initialized_smbc = true; return true; } diff --git a/smb/kio_smb_browse.cpp b/smb/kio_smb_browse.cpp index 398fc377..4666ced6 100644 --- a/smb/kio_smb_browse.cpp +++ b/smb/kio_smb_browse.cpp @@ -1,682 +1,678 @@ ///////////////////////////////////////////////////////////////////////////// // // 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-2020 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 #include #include "wsdiscoverer.h" #include "dnssddiscoverer.h" 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; + qCDebug(KIO_SMB_LOG) << "size " << static_cast(st->st_size); return cacheStatErr; } //--------------------------------------------------------------------------- -int SMBSlave::browse_stat_path(const SMBUrl& _url, UDSEntry& udsentry) +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); } } // 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 // POSIX Access mode must not be mapped either! // It's meaningless for smb shares and downright disadvantagous. // The mode attributes outside the ones used and document above are // useless. The only one actively set is readonlyness. // // BUT the READONLY attribute does nothing on NT systems: // https://support.microsoft.com/en-us/help/326549/you-cannot-view-or-change-the-read-only-or-the-system-attributes-of-fo // The Read-only and System attributes is only used by Windows Explorer to determine // whether the folder is a special folder, such as a system folder that has its view // customized by Windows (for example, My Documents, Favorites, Fonts, Downloaded Program Files), // or a folder that you customized by using the Customize tab of the folder's Properties dialog box. // // As such respecting it on a KIO level is actually wrong as it doesn't indicate actual // readonlyness since the 90s and causes us to show readonly UI states when in fact // the directory is perfectly writable. // https://bugs.kde.org/show_bug.cgi?id=414482 // // Should we ever want to parse desktop.ini like we do .directory we'd only want to when a // dir is readonly as per the above microsoft support article. // Also see: // https://docs.microsoft.com/en-us/windows/win32/shell/how-to-customize-folders-with-desktop-ini udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, -1); udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, st.st_mode & S_IFMT); udsentry.fastInsert(KIO::UDSEntry::UDS_SIZE, st.st_size); 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); statEntry(udsentry); finished(); return; 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; } statEntry(udsentry); finished(); return; } } qCDebug(KIO_SMB_LOG) << "UNKNOWN " << url; finished(); } //=========================================================================== // 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_; 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 ECONNABORTED: return SMBError{ ERR_CONNECTION_BROKEN, url.host() }; case EHOSTUNREACH: return SMBError{ ERR_CANNOT_CONNECT, i18nc("@info:status smb failed to reach the server (e.g. server offline or network failure). %1 is an ip address or hostname", "%1: Host unreachable", url.host()) }; 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() ); + int 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 // Run service discovery if the path is root. This augments // "native" results from libsmbclient. auto normalizedUrl = url.adjusted(QUrl::NormalizePathSegments); if (normalizedUrl.path().isEmpty()) { qCDebug(KIO_SMB_LOG) << "Trying modern discovery (dnssd/wsdiscovery)"; QEventLoop e; UDSEntryList list; const auto flushEntries = [this, &list]() { if (list.isEmpty()) { return; } listEntries(list); list.clear(); }; const auto quitLoop = [&e, &flushEntries]() { flushEntries(); e.quit(); }; // Since slavebase has no eventloop it wont publish results // on a timer, since we do not know how long our discovery // will take this is super meh because we may appear // stuck for a while. Implement our own listing system // based on QTimer to mitigate. QTimer sendTimer; sendTimer.setInterval(300); connect(&sendTimer, &QTimer::timeout, this, flushEntries); sendTimer.start(); DNSSDDiscoverer d; WSDiscoverer w; const QList discoverers {&d, &w}; - auto appendDiscovery = [&](Discovery::Ptr discovery) { + auto appendDiscovery = [&](const Discovery::Ptr &discovery) { list.append(discovery->toEntry()); }; auto maybeFinished = [&] { // finishes if all discoveries finished bool allFinished = true; for (auto discoverer : discoverers) { allFinished = allFinished && discoverer->isFinished(); } if (allFinished) { quitLoop(); } }; connect(&d, &DNSSDDiscoverer::newDiscovery, this, appendDiscovery); connect(&w, &WSDiscoverer::newDiscovery, this, appendDiscovery); connect(&d, &DNSSDDiscoverer::finished, this, maybeFinished); connect(&w, &WSDiscoverer::finished, this, maybeFinished); d.start(); w.start(); QTimer::singleShot(16000, &e, quitLoop); // max execution time! e.exec(); qCDebug(KIO_SMB_LOG) << "Modern discovery finished."; } 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::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; - struct statvfs dirStat; + struct statvfs dirStat{}; memset(&dirStat, 0, sizeof(struct statvfs)); const int err = smbc_statvfs(smbcUrl.toSmbcUrl().data(), &dirStat); if (err < 0) { error(KIO::ERR_CANNOT_STAT, url.url()); return; } // 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 29beed6a..0d47fe9e 100644 --- a/smb/kio_smb_dir.cpp +++ b/smb/kio_smb_dir.cpp @@ -1,784 +1,783 @@ ///////////////////////////////////////////////////////////////////////////// // // 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) - { + while (true) { 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) { + while (true) { 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; + struct utimbuf utbuf{}; 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) { + while (true) { 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; + struct utimbuf utbuf{}; utbuf.actime = st.st_atime; // access time, unchanged utbuf.modtime = dt.toSecsSinceEpoch(); // modification time 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_file.cpp b/smb/kio_smb_file.cpp index e4410da9..bc145289 100644 --- a/smb/kio_smb_file.cpp +++ b/smb/kio_smb_file.cpp @@ -1,482 +1,480 @@ //////////////////////////////////////////////////////////////////////////// // // Project: SMB kioslave for KDE2 // // File: kio_smb_file.cpp // // Abstract: member function implementations for SMBSlave that deal with // SMB file 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 //=========================================================================== void SMBSlave::get( const QUrl& kurl ) { char buf[MAX_XFER_BUF_SIZE]; int filefd = 0; int errNum = 0; ssize_t bytesread = 0; // time_t curtime = 0; // time_t lasttime = 0; // Disabled durint port to Qt5/KF5. Seems to be unused. // time_t starttime = 0; // Disabled durint port to Qt5/KF5. Seems to be unused. KIO::filesize_t totalbytesread = 0; QByteArray filedata; SMBUrl url; qCDebug(KIO_SMB_LOG) << kurl; // check (correct) URL QUrl kvurl = checkURL(kurl); // if URL is not valid we have to redirect to correct URL if (kvurl != kurl) { redirection(kvurl); finished(); return; } if(!auth_initialize_smbc()) return; // Stat url = kurl; errNum = cache_stat(url,&st); if( errNum != 0 ) { if ( errNum == EACCES ) error( KIO::ERR_ACCESS_DENIED, url.toDisplayString()); else error( KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); return; } if ( S_ISDIR( st.st_mode ) ) { error( KIO::ERR_IS_DIRECTORY, url.toDisplayString()); return; } // Set the total size totalSize( st.st_size ); // Open and read the file filefd = smbc_open(url.toSmbcUrl(),O_RDONLY,0); if(filefd >= 0) { bool isFirstPacket = true; // lasttime = starttime = time(NULL); // This seems to be unused.. - while(1) - { + while (true) { bytesread = smbc_read(filefd, buf, MAX_XFER_BUF_SIZE); if(bytesread == 0) { // All done reading break; } else if(bytesread < 0) { error( KIO::ERR_CANNOT_READ, url.toDisplayString()); return; } filedata = QByteArray::fromRawData(buf,bytesread); if (isFirstPacket) { QMimeDatabase db; QMimeType type = db.mimeTypeForFileNameAndData(url.fileName(), filedata); mimeType(type.name()); isFirstPacket = false; } data( filedata ); filedata.clear(); // increment total bytes read totalbytesread += bytesread; processedSize(totalbytesread); } smbc_close(filefd); data( QByteArray() ); processedSize(static_cast(st.st_size)); } else { error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.toDisplayString()); return; } finished(); } //=========================================================================== void SMBSlave::open( const QUrl& kurl, QIODevice::OpenMode mode) { int errNum = 0; qCDebug(KIO_SMB_LOG) << kurl; // check (correct) URL QUrl kvurl = checkURL(kurl); // if URL is not valid we have to redirect to correct URL if (kvurl != kurl) { redirection(kvurl); finished(); return; } if(!auth_initialize_smbc()) { error( KIO::ERR_ACCESS_DENIED, kurl.toDisplayString()); return; } // Save the URL as a private member // FIXME For some reason m_openUrl has be be declared in bottom private // section of the class SMBSlave declaration instead of the top section // or else this assignment fails m_openUrl = kurl; // Stat errNum = cache_stat(m_openUrl,&st); if( errNum != 0 ) { if ( errNum == EACCES ) error( KIO::ERR_ACCESS_DENIED, m_openUrl.toDisplayString()); else error( KIO::ERR_DOES_NOT_EXIST, m_openUrl.toDisplayString()); return; } if ( S_ISDIR( st.st_mode ) ) { error( KIO::ERR_IS_DIRECTORY, m_openUrl.toDisplayString()); return; } // Set the total size totalSize( st.st_size ); // Convert permissions int flags = 0; if (mode & QIODevice::ReadOnly) { if (mode & QIODevice::WriteOnly) { flags = O_RDWR | O_CREAT; } else { flags = O_RDONLY; } } else if (mode & QIODevice::WriteOnly) { flags = O_WRONLY | O_CREAT; } if (mode & QIODevice::Append) { flags |= O_APPEND; } else if (mode & QIODevice::Truncate) { flags |= O_TRUNC; } // Open the file m_openFd = smbc_open(m_openUrl.toSmbcUrl(), flags, 0); if(m_openFd < 0) { error( KIO::ERR_CANNOT_OPEN_FOR_READING, m_openUrl.toDisplayString()); return; } // Determine the mimetype of the file to be retrieved, and emit it. // This is mandatory in all slaves (for KRun/BrowserRun to work). // If we're not opening the file ReadOnly or ReadWrite, don't attempt to // read the file and send the mimetype. if (mode & QIODevice::ReadOnly){ ssize_t bytesRequested = 1024; ssize_t bytesRead = 0; QVarLengthArray buffer(bytesRequested); bytesRead = smbc_read(m_openFd, buffer.data(), bytesRequested); if(bytesRead < 0) { error( KIO::ERR_CANNOT_READ, m_openUrl.toDisplayString()); closeWithoutFinish(); return; } else { QByteArray fileData = QByteArray::fromRawData(buffer.data(),bytesRead); QMimeDatabase db; QMimeType type = db.mimeTypeForFileNameAndData(m_openUrl.fileName(), fileData); mimeType(type.name()); off_t res = smbc_lseek(m_openFd, 0, SEEK_SET); if (res == (off_t)-1) { error(KIO::ERR_CANNOT_SEEK, m_openUrl.path()); closeWithoutFinish(); return; } } } position( 0 ); - emit opened(); + opened(); } void SMBSlave::read( KIO::filesize_t bytesRequested ) { Q_ASSERT(m_openFd != -1); QVarLengthArray buffer(bytesRequested); ssize_t bytesRead = 0; bytesRead = smbc_read(m_openFd, buffer.data(), bytesRequested); Q_ASSERT(bytesRead <= static_cast(bytesRequested)); if(bytesRead < 0) { qCDebug(KIO_SMB_LOG) << "Could not read " << m_openUrl; error( KIO::ERR_CANNOT_READ, m_openUrl.toDisplayString()); closeWithoutFinish(); return; } QByteArray fileData = QByteArray::fromRawData(buffer.data(), bytesRead); data( fileData ); } void SMBSlave::write(const QByteArray &fileData) { Q_ASSERT(m_openFd != -1); QByteArray buf(fileData); ssize_t size = smbc_write(m_openFd, buf.data(), buf.size()); if (size < 0) { qCDebug(KIO_SMB_LOG) << "Could not write to " << m_openUrl; error( KIO::ERR_CANNOT_WRITE, m_openUrl.toDisplayString()); closeWithoutFinish(); return; } written(size); } void SMBSlave::seek(KIO::filesize_t offset) { off_t res = smbc_lseek(m_openFd, static_cast(offset), SEEK_SET); if (res == (off_t)-1) { error(KIO::ERR_CANNOT_SEEK, m_openUrl.path()); closeWithoutFinish(); } else { qCDebug(KIO_SMB_LOG) << "res" << res; position( res ); } } void SMBSlave::truncate(KIO::filesize_t length) { off_t res = smbc_ftruncate(m_openFd, static_cast(length)); if (res < 0) { error(KIO::ERR_CANNOT_TRUNCATE, m_openUrl.path()); closeWithoutFinish(); } else { qCDebug( KIO_SMB_LOG ) << "res" << res; truncated(length); } } void SMBSlave::closeWithoutFinish() { smbc_close(m_openFd); } void SMBSlave::close() { closeWithoutFinish(); finished(); } //=========================================================================== void SMBSlave::put( const QUrl& kurl, int permissions, KIO::JobFlags flags ) { void *buf; size_t bufsize; m_current_url = kurl; int filefd; bool exists; int errNum = 0; off_t retValLSeek = 0; mode_t mode; QByteArray filedata; qCDebug(KIO_SMB_LOG) << kurl; errNum = cache_stat(m_current_url, &st); exists = (errNum == 0); if ( exists && !(flags & KIO::Overwrite) && !(flags & KIO::Resume)) { if (S_ISDIR(st.st_mode)) { qCDebug(KIO_SMB_LOG) << kurl <<" already isdir !!"; error( KIO::ERR_DIR_ALREADY_EXIST, m_current_url.toDisplayString()); } else { qCDebug(KIO_SMB_LOG) << kurl <<" already exist !!"; error( KIO::ERR_FILE_ALREADY_EXIST, m_current_url.toDisplayString()); } return; } if (exists && !(flags & KIO::Resume) && (flags & KIO::Overwrite)) { qCDebug(KIO_SMB_LOG) << "exists try to remove " << m_current_url.toSmbcUrl(); // remove(m_current_url.url().toLocal8Bit()); } if (flags & KIO::Resume) { // append if resuming qCDebug(KIO_SMB_LOG) << "resume " << m_current_url.toSmbcUrl(); filefd = smbc_open(m_current_url.toSmbcUrl(), O_RDWR, 0 ); if (filefd < 0) { errNum = errno; } else { errNum = 0; } retValLSeek = smbc_lseek(filefd, 0, SEEK_END); if (retValLSeek == (off_t)-1) { errNum = errno; } else { errNum = 0; } } else { if (permissions != -1) { mode = permissions | S_IWUSR | S_IRUSR; } else { mode = 600;//0666; } qCDebug(KIO_SMB_LOG) << "NO resume " << m_current_url.toSmbcUrl(); filefd = smbc_open(m_current_url.toSmbcUrl(), O_CREAT | O_TRUNC | O_WRONLY, mode); if (filefd < 0) { errNum = errno; } else { errNum = 0; } } if ( filefd < 0 ) { if ( errNum == EACCES ) { qCDebug(KIO_SMB_LOG) << "error " << kurl <<" access denied !!"; error( KIO::ERR_WRITE_ACCESS_DENIED, m_current_url.toDisplayString()); } else { qCDebug(KIO_SMB_LOG) << "error " << kurl <<" can not open for writing !!"; error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, m_current_url.toDisplayString()); } return; } // Loop until we got 0 (end of data) - while(1) - { + while (true) { qCDebug(KIO_SMB_LOG) << "request data "; dataReq(); // Request for data qCDebug(KIO_SMB_LOG) << "write " << m_current_url.toSmbcUrl(); if (readData(filedata) <= 0) { qCDebug(KIO_SMB_LOG) << "readData <= 0"; break; } qCDebug(KIO_SMB_LOG) << "write " << m_current_url.toSmbcUrl(); buf = filedata.data(); bufsize = filedata.size(); ssize_t size = smbc_write(filefd, buf, bufsize); if ( size < 0) { qCDebug(KIO_SMB_LOG) << "error " << kurl << "could not write !!"; error( KIO::ERR_CANNOT_WRITE, m_current_url.toDisplayString()); return; } qCDebug(KIO_SMB_LOG) << "wrote " << size; } qCDebug(KIO_SMB_LOG) << "close " << m_current_url.toSmbcUrl(); if(smbc_close(filefd) < 0) { qCDebug(KIO_SMB_LOG) << kurl << "could not write !!"; error( KIO::ERR_CANNOT_WRITE, m_current_url.toDisplayString()); return; } // set final permissions, if the file was just created if ( permissions != -1 && !exists ) { // TODO: did the smbc_chmod fail? // TODO: put in call to chmod when it is working! // smbc_chmod(url.toSmbcUrl(),permissions); } #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() ) { if (cache_stat( m_current_url, &st ) == 0) { - struct utimbuf utbuf; + struct utimbuf utbuf{}; utbuf.actime = st.st_atime; // access time, unchanged utbuf.modtime = dt.toSecsSinceEpoch(); // modification time smbc_utime( m_current_url.toSmbcUrl(), &utbuf ); } } } #endif // We have done our job => finish finished(); } diff --git a/smb/kio_smb_internal.cpp b/smb/kio_smb_internal.cpp index b2cc6ff1..7fa26507 100644 --- a/smb/kio_smb_internal.cpp +++ b/smb/kio_smb_internal.cpp @@ -1,158 +1,146 @@ ///////////////////////////////////////////////////////////////////////////// // // 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) +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; +SMBUrl::SMBUrl() = default; +SMBUrl::SMBUrl(const SMBUrl &other) = default; +SMBUrl::~SMBUrl() = default; +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/kio_smb_internal.h b/smb/kio_smb_internal.h index 3c95f93a..7eecc9cc 100644 --- a/smb/kio_smb_internal.h +++ b/smb/kio_smb_internal.h @@ -1,129 +1,130 @@ ///////////////////////////////////////////////////////////////////////////// // // Project: SMB kioslave for KDE2 // // File: kio_smb_internal.h // // Abstract: Utility classes used by SMBSlave // // Author(s): Matthew Peterson // Frank Schwanz //--------------------------------------------------------------------------- // // 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 // ///////////////////////////////////////////////////////////////////////////// #ifndef KIO_SMB_INTERNAL_H_INCLUDED #define KIO_SMB_INTERNAL_H_INCLUDED #include #include #include /** * Types of a 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" * URLTYPE_SHARE_OR_PATH - "smb:/mygroupe/mymachine/myshare/mydir" */ enum SMBUrlType { SMBURLTYPE_UNKNOWN = 0, SMBURLTYPE_ENTIRE_NETWORK = 1, SMBURLTYPE_WORKGROUP_OR_SERVER = 2, SMBURLTYPE_SHARE_OR_PATH = 3 }; //=========================================================================== /** * Class to handle URL's * it can convert QUrl to smbUrl * and Handle UserInfo * it also check the correctness of the URL */ class SMBUrl : public QUrl { public: SMBUrl(); SMBUrl(const SMBUrl&); SMBUrl(const QUrl & kurl); + ~SMBUrl(); SMBUrl& operator=(const SMBUrl&); /** * Appends the specified file and dir to this SMBUrl * "smb://server/share" --> "smb://server/share/filedir" */ void addPath(const QString &filedir); bool cd(const QString &dir); /** * 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" * URLTYPE_SHARE_OR_PATH - "smb:/mygroupe/mymachine/myshare/mydir" */ SMBUrlType getType() const; void setPass( const QString& _txt ) { QUrl::setPassword(_txt); updateCache(); } void setUser( const QString& _txt ) { QUrl::setUserName(_txt); updateCache(); } void setHost( const QString& _txt ) { QUrl::setHost(_txt); updateCache(); } void setPath( const QString& _txt ) { QUrl::setPath(_txt); updateCache(); } /** * Returns the workgroup if it given in url */ // QString getWorkgroup() const; /** * Returns path after workgroup */ // QString getServerShareDir() const; /** * Return a URL that is suitable for libsmbclient */ QByteArray toSmbcUrl() const { return m_surl; } /** * Returns the partial URL. */ SMBUrl partUrl() const; private: /** * Change from QString to QCString (MS Windows's character encoding) */ QByteArray fromUnicode( const QString &_str ) const; void updateCache(); QByteArray m_surl; /** * Type of URL * @see _SMBUrlType */ - mutable SMBUrlType m_type; + mutable SMBUrlType m_type = SMBURLTYPE_UNKNOWN; }; #endif diff --git a/smb/kio_smb_mount.cpp b/smb/kio_smb_mount.cpp index 79ddcb79..3cb60c7b 100644 --- a/smb/kio_smb_mount.cpp +++ b/smb/kio_smb_mount.cpp @@ -1,193 +1,196 @@ /* This file is part of the KDE project Copyright (C) 2000 Alexander Neundorf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kio_smb.h" #include #include #include #include #include #include #include void SMBSlave::special( const QByteArray & data) { qCDebug(KIO_SMB_LOG)<<"Smb::special()"; int tmp; QDataStream stream(data); stream >> tmp; //mounting and umounting are both blocking, "guarded" by a SIGALARM in the future switch (tmp) { case 1: case 3: { - QString remotePath, mountPoint, user; + QString remotePath; + QString mountPoint; + QString user; stream >> remotePath >> mountPoint; QStringList sl=remotePath.split('/'); - QString share,host; + QString share; + QString host; if (sl.count()>=2) { host=sl.at(0).mid(2); share=sl.at(1); qCDebug(KIO_SMB_LOG)<<"special() host -"<< host <<"- share -" << share <<"-"; } remotePath.replace('\\', '/'); // smbmounterplugin sends \\host/share qCDebug(KIO_SMB_LOG) << "mounting: " << remotePath.toLocal8Bit() << " to " << mountPoint.toLocal8Bit(); if (tmp==3) { if (!QDir().mkpath(mountPoint)) { error(KIO::ERR_CANNOT_MKDIR, mountPoint); return; } } SMBUrl smburl(QUrl("smb:///")); smburl.setHost(host); smburl.setPath('/' + share); const int passwordError = checkPassword(smburl); if (passwordError != KJob::NoError && passwordError != KIO::ERR_USER_CANCELED) { error(passwordError, smburl.toString()); return; } // using smbmount instead of "mount -t smbfs", because mount does not allow a non-root // user to do a mount, but a suid smbmnt does allow this KProcess proc; proc.setOutputChannelMode(KProcess::SeparateChannels); proc << "smbmount"; QString options; if ( smburl.userName().isEmpty() ) { user = "guest"; options = "guest"; } else { options = "username=" + smburl.userName(); user = smburl.userName(); if ( ! smburl.password().isEmpty() ) options += ",password=" + smburl.password(); } // TODO: check why the control center uses encodings with a blank char, e.g. "cp 1250" //if ( ! m_default_encoding.isEmpty() ) //options += ",codepage=" + KShell::quoteArg(m_default_encoding); proc << remotePath; proc << mountPoint; proc << "-o" << options; proc.start(); if (!proc.waitForFinished()) { error(KIO::ERR_CANNOT_LAUNCH_PROCESS, "smbmount"+i18n("\nMake sure that the samba package is installed properly on your system.")); return; } QString mybuf = QString::fromLocal8Bit(proc.readAllStandardOutput()); QString mystderr = QString::fromLocal8Bit(proc.readAllStandardError()); qCDebug(KIO_SMB_LOG) << "mount exit " << proc.exitCode() << "stdout:" << mybuf << "\nstderr:" << mystderr; if (proc.exitCode() != 0) { error( KIO::ERR_CANNOT_MOUNT, i18n("Mounting of share \"%1\" from host \"%2\" by user \"%3\" failed.\n%4", share, host, user, mybuf + '\n' + mystderr)); return; } finished(); } break; case 2: case 4: { QString mountPoint; stream >> mountPoint; KProcess proc; proc.setOutputChannelMode(KProcess::SeparateChannels); proc << "smbumount"; proc << mountPoint; proc.start(); if ( !proc.waitForFinished() ) { error(KIO::ERR_CANNOT_LAUNCH_PROCESS, "smbumount"+i18n("\nMake sure that the samba package is installed properly on your system.")); return; } QString mybuf = QString::fromLocal8Bit(proc.readAllStandardOutput()); QString mystderr = QString::fromLocal8Bit(proc.readAllStandardError()); qCDebug(KIO_SMB_LOG) << "smbumount exit " << proc.exitCode() << "stdout:" << mybuf << "\nstderr:" << mystderr; if (proc.exitCode() != 0) { error(KIO::ERR_CANNOT_UNMOUNT, i18n("Unmounting of mountpoint \"%1\" failed.\n%2", mountPoint, mybuf + '\n' + mystderr)); return; } if ( tmp == 4 ) { bool ok; QDir dir(mountPoint); dir.cdUp(); ok = dir.rmdir(mountPoint); if ( ok ) { QString p=dir.path(); dir.cdUp(); ok = dir.rmdir(p); } if ( !ok ) { error(KIO::ERR_CANNOT_RMDIR, mountPoint); return; } } finished(); } break; default: break; } finished(); }