File indexing completed on 2024-05-19 05:00:42
0001 /* 0002 SPDX-FileCopyrightText: 2004, 2005 Jakub Stachowski <qbast@go2.pl> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "dnssd.h" 0008 0009 // kioworker 0010 #include "zeroconfurl.h" 0011 // KF 0012 #include <KLocalizedString> 0013 #include <KProtocolInfo> 0014 // Qt 0015 #include <QCoreApplication> 0016 #include <qplatformdefs.h> // S_IFDIR 0017 0018 0019 class KIOPluginForMetaData : public QObject 0020 { 0021 Q_OBJECT 0022 Q_PLUGIN_METADATA(IID "org.kde.kio.worker.zeroconf" FILE "zeroconf.json") 0023 }; 0024 0025 0026 void ProtocolData::feedUrl( QUrl* url, const RemoteService* remoteService ) const 0027 { 0028 const QMap<QString,QByteArray> serviceTextData = remoteService->textData(); 0029 0030 url->setScheme( protocol ); 0031 if (!userEntry.isNull()) 0032 url->setUserName( QString::fromUtf8(serviceTextData[userEntry]) ); 0033 if (!passwordEntry.isNull()) 0034 url->setPassword( QString::fromUtf8(serviceTextData[passwordEntry]) ); 0035 if (!pathEntry.isNull()) 0036 url->setPath( QString::fromUtf8(serviceTextData[pathEntry]) ); 0037 0038 url->setHost( remoteService->hostName() ); 0039 url->setPort( remoteService->port() ); 0040 } 0041 0042 0043 ZeroConfProtocol::ZeroConfProtocol(const QByteArray& protocol, const QByteArray &pool_socket, const QByteArray &app_socket) 0044 : WorkerBase(protocol, pool_socket, app_socket), 0045 serviceBrowser(nullptr), 0046 serviceTypeBrowser(nullptr), 0047 serviceToResolve(nullptr) 0048 { 0049 knownProtocols[QStringLiteral("_ftp._tcp")]= ProtocolData(i18n("FTP servers"), QStringLiteral("ftp"), QStringLiteral("path"), QStringLiteral("u"), QStringLiteral("p")); 0050 knownProtocols[QStringLiteral("_webdav._tcp")]= ProtocolData(i18n("WebDav remote directory"),QStringLiteral("webdav"), QStringLiteral("path")); 0051 knownProtocols[QStringLiteral("_sftp-ssh._tcp")]=ProtocolData(i18n("Remote disk (sftp)"), QStringLiteral("sftp"), QString(), QStringLiteral("u"), QStringLiteral("p")); 0052 knownProtocols[QStringLiteral("_ssh._tcp")]= ProtocolData(i18n("Remote disk (fish)"), QStringLiteral("fish"), QString(), QStringLiteral("u"), QStringLiteral("p")); 0053 knownProtocols[QStringLiteral("_nfs._tcp")]= ProtocolData(i18n("NFS remote directory"), QStringLiteral("nfs"), QStringLiteral("path")); 0054 } 0055 0056 ZeroConfProtocol::~ZeroConfProtocol() 0057 { 0058 delete serviceToResolve; 0059 } 0060 0061 KIO::WorkerResult ZeroConfProtocol::get( const QUrl& url ) 0062 { 0063 const KIO::WorkerResult dnssdOkResult = dnssdOK(); 0064 if (!dnssdOkResult.success()) { 0065 return dnssdOkResult; 0066 } 0067 0068 const ZeroConfUrl zeroConfUrl( url ); 0069 0070 ZeroConfUrl::Type type = zeroConfUrl.type(); 0071 if (type != ZeroConfUrl::Service) { 0072 return KIO::WorkerResult::fail(KIO::ERR_MALFORMED_URL, url.toDisplayString()); 0073 } 0074 0075 return resolveAndRedirect( zeroConfUrl ); 0076 } 0077 0078 KIO::WorkerResult ZeroConfProtocol::mimetype( const QUrl& url ) 0079 { 0080 return resolveAndRedirect( ZeroConfUrl(url) ); 0081 } 0082 0083 KIO::WorkerResult ZeroConfProtocol::stat( const QUrl& url ) 0084 { 0085 const KIO::WorkerResult dnssdOkResult = dnssdOK(); 0086 if (!dnssdOkResult.success()) { 0087 return dnssdOkResult; 0088 } 0089 0090 const ZeroConfUrl zeroConfUrl( url ); 0091 0092 ZeroConfUrl::Type type = zeroConfUrl.type(); 0093 0094 switch (type) 0095 { 0096 case ZeroConfUrl::RootDir: 0097 case ZeroConfUrl::ServiceDir: 0098 { 0099 UDSEntry entry; 0100 feedEntryAsDir( &entry, QString() ); 0101 statEntry( entry ); 0102 return KIO::WorkerResult::pass(); 0103 } 0104 case ZeroConfUrl::Service: 0105 return resolveAndRedirect( zeroConfUrl ); 0106 break; 0107 default: 0108 return KIO::WorkerResult::fail(KIO::ERR_MALFORMED_URL, url.toDisplayString()); 0109 } 0110 } 0111 0112 KIO::WorkerResult ZeroConfProtocol::listDir( const QUrl& url ) 0113 { 0114 const KIO::WorkerResult dnssdOkResult = dnssdOK(); 0115 if (!dnssdOkResult.success()) { 0116 return dnssdOkResult; 0117 } 0118 0119 const ZeroConfUrl zeroConfUrl( url ); 0120 0121 ZeroConfUrl::Type type = zeroConfUrl.type(); 0122 switch (type) 0123 { 0124 case ZeroConfUrl::RootDir: 0125 listCurrentDirEntry(); 0126 serviceTypeBrowser = new ServiceTypeBrowser(zeroConfUrl.domain()); 0127 connect(serviceTypeBrowser, &ServiceTypeBrowser::serviceTypeAdded, 0128 this, &ZeroConfProtocol::addServiceType); 0129 connect(serviceTypeBrowser, &ServiceTypeBrowser::finished, 0130 this, &ZeroConfProtocol::onBrowserFinished); 0131 serviceTypeBrowser->startBrowse(); 0132 enterLoop(); 0133 return KIO::WorkerResult::pass(); 0134 case ZeroConfUrl::ServiceDir: 0135 if( !knownProtocols.contains(zeroConfUrl.serviceType()) ) 0136 { 0137 return KIO::WorkerResult::fail(KIO::ERR_SERVICE_NOT_AVAILABLE, zeroConfUrl.serviceType()); 0138 } 0139 listCurrentDirEntry(); 0140 serviceBrowser = new ServiceBrowser( zeroConfUrl.serviceType(), false, zeroConfUrl.domain() ); 0141 connect(serviceBrowser, &ServiceBrowser::serviceAdded, 0142 this, &ZeroConfProtocol::addService); 0143 connect(serviceBrowser, &ServiceBrowser::finished, 0144 this, &ZeroConfProtocol::onBrowserFinished); 0145 serviceBrowser->startBrowse(); 0146 enterLoop(); 0147 return KIO::WorkerResult::pass(); 0148 case ZeroConfUrl::Service: 0149 return resolveAndRedirect( zeroConfUrl ); 0150 default: 0151 return KIO::WorkerResult::fail(KIO::ERR_MALFORMED_URL, url.toDisplayString()); 0152 } 0153 } 0154 0155 KIO::WorkerResult ZeroConfProtocol::dnssdOK() 0156 { 0157 switch (ServiceBrowser::isAvailable()) 0158 { 0159 case ServiceBrowser::Stopped: 0160 return KIO::WorkerResult::fail( KIO::ERR_UNSUPPORTED_ACTION, 0161 i18n("The Zeroconf daemon (mdnsd) is not running.")); 0162 case ServiceBrowser::Unsupported: 0163 return KIO::WorkerResult::fail( KIO::ERR_UNSUPPORTED_ACTION, 0164 i18n("The KDNSSD library has been built without Zeroconf support.")); 0165 default: 0166 return KIO::WorkerResult::pass(); 0167 } 0168 } 0169 0170 KIO::WorkerResult ZeroConfProtocol::resolveAndRedirect( const ZeroConfUrl& zeroConfUrl ) 0171 { 0172 if (serviceToResolve && !zeroConfUrl.matches(serviceToResolve)) 0173 { 0174 delete serviceToResolve; 0175 serviceToResolve = nullptr; 0176 } 0177 if (serviceToResolve == nullptr) 0178 { 0179 if( !knownProtocols.contains(zeroConfUrl.serviceType()) ) 0180 { 0181 return KIO::WorkerResult::fail(KIO::ERR_SERVICE_NOT_AVAILABLE, zeroConfUrl.serviceType()); 0182 } 0183 0184 serviceToResolve = new RemoteService( zeroConfUrl.serviceName(), zeroConfUrl.serviceType(), zeroConfUrl.domain() ); 0185 if (!serviceToResolve->resolve()) 0186 { 0187 return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, zeroConfUrl.serviceName()); 0188 } 0189 } 0190 0191 if( !knownProtocols.contains(zeroConfUrl.serviceType()) ) 0192 return KIO::WorkerResult::fail(); 0193 0194 // action 0195 const ProtocolData& protocolData = knownProtocols[zeroConfUrl.serviceType()]; 0196 QUrl destUrl; 0197 protocolData.feedUrl( &destUrl, serviceToResolve ); 0198 0199 redirection( destUrl ); 0200 0201 return KIO::WorkerResult::pass(); 0202 } 0203 0204 void ZeroConfProtocol::listCurrentDirEntry() 0205 { 0206 UDSEntry entry; 0207 feedEntryAsDir( &entry, QStringLiteral(".") ); 0208 listEntry(entry); 0209 } 0210 0211 void ZeroConfProtocol::addServiceType( const QString& serviceType ) 0212 { 0213 if (ServiceTypesAdded.contains(serviceType)) 0214 return; 0215 ServiceTypesAdded << serviceType; 0216 0217 if (!knownProtocols.contains(serviceType)) 0218 return; 0219 0220 // action 0221 UDSEntry entry; 0222 feedEntryAsDir( &entry, serviceType, knownProtocols[serviceType].name ); 0223 listEntry( entry ); 0224 } 0225 0226 void ZeroConfProtocol::addService( KDNSSD::RemoteService::Ptr service ) 0227 { 0228 UDSEntry entry; 0229 entry.fastInsert(UDSEntry::UDS_NAME, service->serviceName() ); 0230 entry.fastInsert( UDSEntry::UDS_ACCESS, 0666); 0231 entry.fastInsert( UDSEntry::UDS_FILE_TYPE, S_IFDIR ); 0232 const QString iconName = KProtocolInfo::icon( knownProtocols[service->type()].protocol ); 0233 if (!iconName.isNull()) 0234 entry.fastInsert( UDSEntry::UDS_ICON_NAME, iconName ); 0235 0236 listEntry( entry ); 0237 } 0238 0239 void ZeroConfProtocol::onBrowserFinished() 0240 { 0241 // cleanup 0242 if (serviceBrowser) 0243 { 0244 serviceBrowser->deleteLater(); 0245 serviceBrowser = nullptr; 0246 } 0247 if (serviceTypeBrowser) 0248 { 0249 serviceTypeBrowser->deleteLater(); 0250 serviceTypeBrowser = nullptr; 0251 } 0252 ServiceTypesAdded.clear(); 0253 0254 Q_EMIT leaveModality(); 0255 } 0256 0257 void ZeroConfProtocol::feedEntryAsDir( UDSEntry* entry, const QString& name, const QString& displayName ) 0258 { 0259 entry->fastInsert( UDSEntry::UDS_NAME, name ); 0260 entry->fastInsert( UDSEntry::UDS_ACCESS, 0555 ); 0261 entry->fastInsert( UDSEntry::UDS_FILE_TYPE, S_IFDIR ); 0262 entry->fastInsert( UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory") ); 0263 if (!displayName.isEmpty()) 0264 entry->fastInsert( UDSEntry::UDS_DISPLAY_NAME, displayName ); 0265 } 0266 0267 void ZeroConfProtocol::enterLoop() 0268 { 0269 QEventLoop eventLoop; 0270 connect(this, &ZeroConfProtocol::leaveModality, &eventLoop, &QEventLoop::quit); 0271 eventLoop.exec(QEventLoop::ExcludeUserInputEvents); 0272 } 0273 0274 0275 extern "C" Q_DECL_EXPORT int kdemain( int argc, char **argv ) 0276 { 0277 // necessary to use other KIO workers 0278 QCoreApplication app(argc,argv); 0279 app.setApplicationName(QStringLiteral("kio_zeroconf")); 0280 0281 if (argc != 4) { 0282 fprintf(stderr, "Usage: %s protocol domain-socket1 domain-socket2\n", argv[0]); 0283 exit(-1); 0284 } 0285 0286 // start the worker 0287 ZeroConfProtocol worker(argv[1],argv[2],argv[3]); 0288 worker.dispatchLoop(); 0289 return 0; 0290 } 0291 0292 #include "dnssd.moc" 0293 #include "moc_dnssd.cpp"