File indexing completed on 2024-04-28 04:58:02
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2000 Caldera Systems Inc. 0004 SPDX-FileCopyrightText: 2020-2021 Harald Sitter <sitter@kde.org> 0005 SPDX-FileContributor: Matthew Peterson <mpeterson@caldera.com> 0006 */ 0007 0008 #include "smburl.h" 0009 #include "smb-logsettings.h" 0010 0011 #include <KConfig> 0012 #include <KIO/Global> 0013 #include <QDir> 0014 #include <QHostAddress> 0015 #include <QUrlQuery> 0016 0017 SMBUrl::SMBUrl(const QUrl &kurl) 0018 : QUrl(kurl) 0019 { 0020 // We treat cifs as an alias but need to translate it to smb. 0021 // https://bugs.kde.org/show_bug.cgi?id=327295 0022 // It's not IANA registered and also libsmbc internally expects 0023 // smb URIs so we do very broadly coerce cifs to smb. 0024 // Also see SMBWorker::checkURL. 0025 if (scheme() == "cifs") { 0026 setScheme("smb"); 0027 } 0028 updateCache(); 0029 } 0030 0031 SMBUrl::SMBUrl() = default; 0032 SMBUrl::SMBUrl(const SMBUrl &other) = default; 0033 SMBUrl::~SMBUrl() = default; 0034 SMBUrl &SMBUrl::operator=(const SMBUrl &) = default; 0035 0036 void SMBUrl::addPath(const QString &filedir) 0037 { 0038 if (path().length() > 0 && path().at(path().length() - 1) != QLatin1Char('/')) { 0039 QUrl::setPath(path() + QLatin1Char('/') + filedir); 0040 } else { 0041 QUrl::setPath(path() + filedir); 0042 } 0043 updateCache(); 0044 } 0045 0046 void SMBUrl::cdUp() 0047 { 0048 setUrl(KIO::upUrl(*this).url()); 0049 updateCache(); 0050 } 0051 0052 void SMBUrl::updateCache() 0053 { 0054 QUrl::setPath(QDir::cleanPath(path())); 0055 0056 // SMB URLs are UTF-8 encoded 0057 qCDebug(KIO_SMB_LOG) << "updateCache " << QUrl::path(); 0058 0059 QUrl sambaUrl(*this); 0060 0061 const QHostAddress address(sambaUrl.host()); 0062 switch (address.protocol()) { 0063 case QAbstractSocket::IPv6Protocol: { 0064 // Convert to Windows IPv6 literal to bypass limitations in samba. 0065 // https://bugzilla.samba.org/show_bug.cgi?id=14297 0066 // https://docs.microsoft.com/en-us/windows/win32/api/winnetwk/nf-winnetwk-wnetaddconnection2a 0067 // https://devblogs.microsoft.com/oldnewthing/20100915-00/?p=12863 0068 // https://www.samba.org/~idra/code/nss-ipv6literal/README.html 0069 // https://ipv6-literal.com 0070 QString literal = address.toString(); 0071 literal.replace(':', '-'); // address 0072 literal.replace('%', 's'); // scope 0073 if (literal.front() == '-') { 0074 // Special prefix for [::f] so it doesn't start with a dash. 0075 literal.prepend('0'); 0076 } 0077 if (literal.back() == '-') { 0078 // Special suffix, also cannot end with a dash. 0079 literal.append('0'); 0080 } 0081 literal += ".ipv6-literal.net"; // reserved host host 0082 qCDebug(KIO_SMB_LOG) << "converting IPv6 to literal " << host() << literal; 0083 sambaUrl.setHost(literal); 0084 break; 0085 } 0086 case QAbstractSocket::IPv4Protocol: 0087 case QAbstractSocket::AnyIPProtocol: 0088 case QAbstractSocket::UnknownNetworkLayerProtocol: 0089 break; 0090 } 0091 0092 // NetBios workgroup names may contain characters that QUrl will not 0093 // allow in a host. Yet the SMB URI requires us to have the workgroup 0094 // in the host field when browsing a workgroup. 0095 // As a hacky workaround we'll not set a host but use a query param 0096 // when encountering a workgroup that causes QUrl to error out. 0097 // For libsmbc we then need to translate the query back to SMB URI. 0098 // Since this is super daft string construction it will doubltlessly 0099 // be imperfect and so we do still prefer deferring the string 0100 // construction to QUrl whenever possible. 0101 // https://support.microsoft.com/en-gb/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and 0102 // https://bugs.kde.org/show_bug.cgi?id=204423 0103 // 0104 // Should we ever stop supporting workgroup browsing this entire 0105 // hack can be removed. 0106 QUrlQuery query(sambaUrl); 0107 const QString workgroup = query.queryItemValue("kio-workgroup"); 0108 if (workgroup.isEmpty()) { 0109 // If we don't have a hack to apply we can simply defer to QUrl 0110 if (sambaUrl.url() == "smb:/") { 0111 m_surl = "smb://"; 0112 } else { 0113 m_surl = sambaUrl.toString(QUrl::PrettyDecoded).toUtf8(); 0114 } 0115 } else { 0116 // If we have a workgroup hack to apply we need to manually construct 0117 // the stringy URI. 0118 query.removeQueryItem("kio-workgroup"); 0119 sambaUrl.setQuery(query); 0120 0121 QString url; 0122 url = "smb://"; 0123 if (!sambaUrl.userInfo().isEmpty()) { 0124 url += sambaUrl.userInfo() + "@"; 0125 } 0126 url += workgroup; 0127 // Workgroups can have ports per the IANA definition of smb. 0128 if (sambaUrl.port() != -1) { 0129 url += ':' + QString::number(sambaUrl.port()); 0130 } 0131 0132 // Make sure to only use clear paths. libsmbc is allergic to excess slashes. 0133 QString path('/'); 0134 if (!sambaUrl.host().isEmpty()) { 0135 path += sambaUrl.host(); 0136 } 0137 if (!sambaUrl.path().isEmpty()) { 0138 path += sambaUrl.path(); 0139 } 0140 url += QDir::cleanPath(path); 0141 0142 if (!sambaUrl.query().isEmpty()) { 0143 url += '?' + sambaUrl.query(); 0144 } 0145 if (!sambaUrl.fragment().isEmpty()) { 0146 url += '#' + sambaUrl.fragment(); 0147 } 0148 m_surl = url.toUtf8(); 0149 } 0150 0151 m_type = SMBURLTYPE_UNKNOWN; 0152 // update m_type 0153 (void)getType(); 0154 } 0155 0156 SMBUrlType SMBUrl::getType() const 0157 { 0158 if (m_type != SMBURLTYPE_UNKNOWN) 0159 return m_type; 0160 0161 if (scheme() != "smb") { 0162 m_type = SMBURLTYPE_UNKNOWN; 0163 return m_type; 0164 } 0165 0166 if (QUrlQuery query(*this); query.queryItemValue("kio-printer") == "true") { 0167 return m_type = SMBURLTYPE_PRINTER; 0168 } 0169 0170 if (path().isEmpty() || path(QUrl::FullyDecoded) == "/") { 0171 if (host().isEmpty() && !query().contains("kio-workgroup")) 0172 m_type = SMBURLTYPE_ENTIRE_NETWORK; 0173 else 0174 m_type = SMBURLTYPE_WORKGROUP_OR_SERVER; 0175 return m_type; 0176 } 0177 0178 // Check for the path if we get this far 0179 m_type = SMBURLTYPE_SHARE_OR_PATH; 0180 0181 return m_type; 0182 } 0183 0184 SMBUrl SMBUrl::partUrl() const 0185 { 0186 const bool isRemoteFile = m_type == SMBURLTYPE_SHARE_OR_PATH && !fileName().isEmpty(); 0187 const bool isLocalFile = scheme() == QLatin1String("file") && !fileName().isEmpty(); 0188 // Mind that filename doesn't necessarily mean it is a file. 0189 if (isRemoteFile || isLocalFile) { 0190 SMBUrl url(*this); 0191 url.setPath(path() + QLatin1String(".part")); 0192 return url; 0193 } 0194 0195 return SMBUrl(); 0196 }