/*************************************************************************** * Copyright (C) 2004, 2005 by Jakub Stachowski * * qbast@go2.pl * * * * 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 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dnssd.h" static const KCmdLineOptions options[] = { { "+protocol", I18N_NOOP( "Protocol name" ), 0 }, { "+pool", I18N_NOOP( "Socket name" ), 0 }, { "+app", I18N_NOOP( "Socket name" ), 0 }, KCmdLineLastOption }; ZeroConfProtocol::ZeroConfProtocol(const TQCString& protocol, const TQCString &pool_socket, const TQCString &app_socket) : SlaveBase(protocol, pool_socket, app_socket), browser(0),toResolve(0), configData(0) {} ZeroConfProtocol::~ZeroConfProtocol() { delete configData; } void ZeroConfProtocol::get(const KURL& url ) { if (!dnssdOK()) return; UrlType t = checkURL(url); switch (t) { case HelperProtocol: { resolveAndRedirect(url,true); mimeType("text/html"); TQString reply= "\n"; reply+="\n\n

"+i18n("Requested service has been launched in separate window."); reply+="

\n"; data(reply.utf8()); data(TQByteArray()); finished(); break; } case Service: resolveAndRedirect(url); break; default: error(ERR_MALFORMED_URL,i18n("invalid URL")); } } void ZeroConfProtocol::mimetype(const KURL& url ) { resolveAndRedirect(url); } UrlType ZeroConfProtocol::checkURL(const KURL& url) { if (url.path()=="/") return RootDir; TQString service, type, domain; dissect(url,service,type,domain); const TQString& proto = type.section('.',1,-1); if (type[0]!='_' || (proto!="_udp" && proto!="_tcp")) return Invalid; if (service.isEmpty()) return ServiceDir; if (!domain.isEmpty()) { if (!setConfig(type)) return Invalid; if (!configData->readEntry("Exec").isNull()) return HelperProtocol; return (KProtocolInfo::isHelperProtocol( configData->readEntry( "Protocol", type.section(".",0,0).mid(1)))) ? HelperProtocol : Service; } return Invalid; } // URL zeroconf://domain/_http._tcp/some%20service // URL invitation://host:port/_http._tcp/some%20service?u=username&root=directory void ZeroConfProtocol::dissect(const KURL& url,TQString& name,TQString& type,TQString& domain) { type = url.path().section("/",1,1); domain = url.host(); name = url.path().section("/",2,-1); } bool ZeroConfProtocol::dnssdOK() { switch(ServiceBrowser::isAvailable()) { case ServiceBrowser::Stopped: error(KIO::ERR_UNSUPPORTED_ACTION, i18n("The Zeroconf daemon (mdnsd) is not running.")); return false; case ServiceBrowser::Unsupported: error(KIO::ERR_UNSUPPORTED_ACTION, i18n("KDE has been built without Zeroconf support.")); return false; default: return true; } } void ZeroConfProtocol::stat(const KURL& url) { UDSEntry entry; if (!dnssdOK()) return; UrlType t = checkURL(url); switch (t) { case RootDir: case ServiceDir: buildDirEntry(entry,""); statEntry(entry); finished(); break; case Service: resolveAndRedirect(url); break; case HelperProtocol: { TQString name,type,domain; dissect(url,name,type,domain); buildServiceEntry(entry,name,type,domain); statEntry(entry); finished(); break; } default: error(ERR_MALFORMED_URL,i18n("invalid URL")); } } TQString ZeroConfProtocol::getAttribute(const TQString& name) { TQString entry = configData->readEntry(name); return (entry.isNull()) ? TQString() : toResolve->textData()[entry]; } void ZeroConfProtocol::resolveAndRedirect(const KURL& url, bool useKRun) { TQString name,type,domain; dissect(url,name,type,domain); if (url.protocol()=="invitation") { delete toResolve; toResolve=0; toResolve= new RemoteService(url); if (!toResolve->isResolved()) error(ERR_MALFORMED_URL,i18n("Invalid URL")); } else { kdDebug() << "Resolve for " << name << ", " << type << ", " << domain << "\n"; if (toResolve!=0) if (toResolve->serviceName()==name && toResolve->type()==type && toResolve->domain()==domain && toResolve->isResolved()) { } else { delete toResolve; toResolve = 0; } if (toResolve==0) { toResolve = new RemoteService(name,type,domain); // or maybe HOST_NOT_FOUND? if (!toResolve->resolve()) error(ERR_SERVICE_NOT_AVAILABLE,i18n("Unable to resolve service")); } } KURL destUrl; kdDebug() << "Resolved: " << toResolve->hostName() << "\n"; destUrl.setProtocol(getProtocol(type)); destUrl.setUser(getAttribute("UserEntry")); destUrl.setPass(getAttribute("PasswordEntry")); destUrl.setPath(getAttribute("PathEntry")); destUrl.setHost(toResolve->hostName()); destUrl.setPort(toResolve->port()); // get exec from config or try getting it from helper protocol if (useKRun) KRun::run(configData->readEntry("Exec",KProtocolInfo::exec(getProtocol(type))),destUrl); else { redirection(destUrl); finished(); } } bool ZeroConfProtocol::setConfig(const TQString& type) { kdDebug() << "Setting config for " << type << endl; if (configData) { if (configData->readEntry("Type")!=type) { delete configData; configData=0L; } else return true; } configData = new KConfig("zeroconf/"+type,false,false,"data"); return (configData->readEntry("Type")==type); } inline void buildAtom(UDSEntry& entry,UDSAtomTypes type, const TQString& data) { UDSAtom atom; atom.m_uds=type; atom.m_str=data; entry.append(atom); } inline void buildAtom(UDSEntry& entry,UDSAtomTypes type, long data) { UDSAtom atom; atom.m_uds=type; atom.m_long=data; entry.append(atom); } void ZeroConfProtocol::buildDirEntry(UDSEntry& entry,const TQString& name,const TQString& type, const TQString& host) { entry.clear(); buildAtom(entry,UDS_NAME,name); buildAtom(entry,UDS_ACCESS,0555); buildAtom(entry,UDS_SIZE,0); buildAtom(entry,UDS_FILE_TYPE,S_IFDIR); buildAtom(entry,UDS_MIME_TYPE,"inode/directory"); if (!type.isNull()) buildAtom(entry,UDS_URL,"zeroconf:/"+((!host.isNull()) ? "/"+host+"/" : "" )+type+"/"); } TQString ZeroConfProtocol::getProtocol(const TQString& type) { setConfig(type); return configData->readEntry("Protocol",type.section(".",0,0).mid(1)); } void ZeroConfProtocol::buildServiceEntry(UDSEntry& entry,const TQString& name,const TQString& type,const TQString& domain) { setConfig(type); entry.clear(); buildAtom(entry,UDS_NAME,name); buildAtom(entry,UDS_ACCESS,0666); TQString icon=configData->readEntry("Icon",KProtocolInfo::icon(getProtocol(type))); if (!icon.isNull()) buildAtom(entry,UDS_ICON_NAME,icon); KURL protourl; protourl.setProtocol(getProtocol(type)); TQString encname = "zeroconf://" + domain +"/" +type+ "/" + name; if (KProtocolInfo::supportsListing(protourl)) { buildAtom(entry,UDS_FILE_TYPE,S_IFDIR); encname+="/"; } else buildAtom(entry,UDS_FILE_TYPE,S_IFREG); buildAtom(entry,UDS_URL,encname); } void ZeroConfProtocol::listDir(const KURL& url ) { if (!dnssdOK()) return; UrlType t = checkURL(url); UDSEntry entry; switch (t) { case RootDir: if (allDomains=url.host().isEmpty()) browser = new ServiceBrowser(ServiceBrowser::AllServices); else browser = new ServiceBrowser(ServiceBrowser::AllServices,url.host()); connect(browser,TQT_SIGNAL(serviceAdded(DNSSD::RemoteService::Ptr)), this,TQT_SLOT(newType(DNSSD::RemoteService::Ptr))); break; case ServiceDir: if (url.host().isEmpty()) browser = new ServiceBrowser(url.path(-1).section("/",1,-1)); else browser = new ServiceBrowser(url.path(-1).section("/",1,-1),url.host()); connect(browser,TQT_SIGNAL(serviceAdded(DNSSD::RemoteService::Ptr)), this,TQT_SLOT(newService(DNSSD::RemoteService::Ptr))); break; case Service: resolveAndRedirect(url); return; default: error(ERR_MALFORMED_URL,i18n("invalid URL")); return; } connect(browser,TQT_SIGNAL(finished()),this,TQT_SLOT(allReported())); browser->startBrowse(); kapp->eventLoop()->enterLoop(); } void ZeroConfProtocol::allReported() { UDSEntry entry; listEntry(entry,true); finished(); delete browser; browser=0; mergedtypes.clear(); kapp->eventLoop()->exitLoop(); } void ZeroConfProtocol::newType(DNSSD::RemoteService::Ptr srv) { if (mergedtypes.tqcontains(srv->type())>0) return; mergedtypes << srv->type(); UDSEntry entry; kdDebug() << "Got new entry " << srv->type() << endl; if (!setConfig(srv->type())) return; TQString name = configData->readEntry("Name"); if (!name.isNull()) { buildDirEntry(entry,name,srv->type(), (allDomains) ? TQString() : browser->browsedDomains()->domains()[0]); listEntry(entry,false); } } void ZeroConfProtocol::newService(DNSSD::RemoteService::Ptr srv) { UDSEntry entry; buildServiceEntry(entry,srv->serviceName(),srv->type(),srv->domain()); listEntry(entry,false); } extern "C" { int KDE_EXPORT kdemain( int argc, char **argv ) { // KApplication is necessary to use other ioslaves putenv(strdup("SESSION_MANAGER=")); KCmdLineArgs::init(argc, argv, "kio_zeroconf", 0, 0, 0, 0); KCmdLineArgs::addCmdLineOptions( options ); KApplication::disableAutoDcopRegistration(); KApplication app; KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); ZeroConfProtocol slave( args->arg(0), args->arg(1), args->arg(2) ); slave.dispatchLoop(); return 0; } } #include "dnssd.moc"