/* * This file is part of the KFTPGrabber project * * Copyright (C) 2003-2004 by the KFTPGrabber developers * Copyright (C) 2003-2004 Jernej Kos * * 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 * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston, * MA 02110-1301, USA. * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ #include "kftpbookmarks.h" #include "kftpbookmarkaction.h" #include "kftpqueue.h" #include "kftpapi.h" #include "kftpwalletconnection.h" #include "misc.h" #include "desencryptor.h" #include "browser/view.h" #include "kftpsession.h" #include "bookmarks/listview.h" #include "misc/kftpconfig.h" #include "engine/thread.h" #include "engine/ftpsocket.h" #include #include #include #include #include #include #include #include #include #include /* KSSL includes */ #include using namespace KFTPGrabberBase; namespace KFTPBookmarks { Site::Site(TQDomNode node) : m_element(node.toElement()) { // generate id if it is not present! if (m_element.tagName() == "category") { m_type = ST_CATEGORY; if (getAttribute("id").isEmpty()) setAttribute("id", TQString("cat-%1").arg(TDEApplication::randomString(7))); } else if (m_element.tagName() == "server") { m_type = ST_SITE; if (getAttribute("id").isEmpty()) setAttribute("id", TQString("site-%1").arg(TDEApplication::randomString(7))); } // Set the id m_id = getAttribute("id"); } Site::~Site() { } Site *Site::duplicate() { Site *site = new Site(m_element.cloneNode()); site->setAttribute("name", i18n("Copy of") + " " + getAttribute("name")); // Assign a new id site->setAttribute("id", TQString("site-%1").arg(TDEApplication::randomString(7))); site->m_id = site->getAttribute("id"); m_element.parentNode().appendChild(site->m_element); return site; } void Site::reparentSite(Site *site) { // Move site's parent m_element.appendChild(site->m_element); } Site *Site::addSite(TQDomNode node) { if (node.isNull()) { // If there was no node specified, create a new one node = m_element.ownerDocument().createElement("server"); } Site *site = new Site(node); m_element.appendChild(site->m_element); return site; } void Site::addCategory(const TQString &name) { TQDomElement cat = m_element.ownerDocument().createElement("category"); // Assign a new id and name cat.setAttribute("id", TQString("cat-%1").arg(TDEApplication::randomString(7))); cat.setAttribute("name", name); m_element.appendChild(cat); } KURL Site::getUrl() { KURL url; url.setProtocol(getProperty("protocol")); url.setHost(getProperty("host")); url.setPort(getIntProperty("port")); url.setUser(getProperty("username")); url.setPass(getProperty("password")); return url; } Site *Site::getParentSite() { // Get parent's site id, then search for it TQDomElement parent = m_element.parentNode().toElement(); if (parent.isNull()) return NULL; else return KFTPBookmarks::Manager::self()->findCategory(parent.attribute("id")); } TQString Site::getProperty(const TQString &name) { TQDomNodeList nodes = m_element.elementsByTagName(name); if (nodes.length() > 0) { TQString property = nodes.item(0).toElement().text(); property.stripWhiteSpace(); // Automagicly decode passwords from BASE64 if (name.contains("pass") == 1) property = decodePassword(property); return property; } else { return TQString::null; } } int Site::getIntProperty(const TQString &name) { return getProperty(name).toInt(); } void Site::setProperty(const TQString &name, const TQString &value) { // First delete the old property if it exists TQDomNodeList nodes = m_element.elementsByTagName(name); if (nodes.length() > 0) m_element.removeChild(nodes.item(0)); // Now add a new one TQDomElement property = m_element.ownerDocument().createElement(name); m_element.appendChild(property); TQDomText text = m_element.ownerDocument().createTextNode(value); property.appendChild(text); } void Site::setProperty(const TQString &name, int value) { setProperty(name, TQString::number(value)); } void Site::setAttribute(const TQString &name, const TQString &value) { m_element.setAttribute(name, value); } TQString Site::getAttribute(const TQString &name) { return m_element.attribute(name); } //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// Manager *Manager::m_self = 0; static KStaticDeleter staticBookmarkManagerDeleter; Manager *Manager::self() { if (!m_self) { staticBookmarkManagerDeleter.setObject(m_self, new Manager()); } return m_self; } Manager::Manager() : TQObject() { // Init the cache m_siteCache.setAutoDelete(true); // Init the DOM document m_document = TQDomDocument("KFTPGrabber"); } Manager::Manager(const Manager &bookmarks) : TQObject() { // Init the cache m_siteCache.setAutoDelete(true); // Init the DOM document m_document = TQDomDocument("KFTPGrabber"); // Copy the bookmarks TQDomNode tmp = m_document.importNode(bookmarks.m_document.documentElement(), true); m_document.appendChild(tmp); } Manager::~Manager() { if (m_self == this) staticBookmarkManagerDeleter.setObject(m_self, 0, false); } void Manager::setBookmarks(KFTPBookmarks::Manager *bookmarks) { // Init the DOM document m_document = TQDomDocument("KFTPGrabber"); TQDomNode tmp = m_document.importNode(bookmarks->m_document.documentElement(), true); m_document.appendChild(tmp); // Clear the cache m_siteCache.clear(); emit update(); } void Manager::importSites(TQDomNode node) { TQDomNode import = m_document.importNode(node, true); m_document.documentElement().appendChild(import); // Run sanity checks to generate missing ids Manager::validate(); } void Manager::load(const TQString &filename) { m_filename = filename; TQFile file(filename); if (!file.open(IO_ReadOnly)) { // Create a new empty XML m_document.setContent(TQString("").arg(KFTP_BOOKMARKS_VERSION)); return; } // Check if the file is encrpyted TQCString content(file.readAll()); if (KFTPCore::Config::encryptBookmarks()) { // File is encrypted pwd_entry: int saveToWallet = 1; TQCString p_pass(KFTPAPI::getInstance()->walletConnection()->getPassword("bookmarkDecryptPwd").ascii()); if (TQString(p_pass).isNull()) { // Ask the user for a password int ret = KPasswordDialog::getPassword(p_pass, i18n("This bookmark file is encrypted. Please enter key for decryption."), &saveToWallet); if (ret != KPasswordDialog::Accepted) { // User pressed cancel p_pass = ""; } } // Try to decrypt DESEncryptor des; des.setKey(p_pass); des.decrypt(content); if (des.output().left(6) != "walletConnection()->setPassword("bookmarkDecryptPwd", TQString::null); if (KMessageBox::warningContinueCancel( KFTPAPI::getInstance()->mainWindow(), i18n("Bookmark file decryption has failed with provided key. Do you want to overwrite bookmarks with an empty file ?

Warning: If you overwrite, all current bookmarks will be lost.
"), i18n("Decryption Failed"), KGuiItem(i18n("&Overwrite Bookmarks")) ) != KMessageBox::Continue) { // Request the password again goto pwd_entry; } // Create new empty XML m_document.setContent(TQString("").arg(KFTP_BOOKMARKS_VERSION)); file.close(); return; } // Save the password for later encryption m_decryptKey = p_pass; content = des.output().ascii(); if (saveToWallet) { // Save the password to TDEWallet KFTPAPI::getInstance()->walletConnection()->setPassword("bookmarkDecryptPwd", p_pass); } } m_document.setContent(TQString::fromLocal8Bit(content)); file.close(); // Check for XML document version updates versionUpdate(); // Document validation Manager::validate(); // We have just loaded the bookmarks, so update all the menus emit update(); } void Manager::save() { // Save the new XML file if (m_filename.isEmpty()) { tqDebug("WARNING: No open XML file, will NOT save."); return; } TQFile file(m_filename); if (!file.open(IO_WriteOnly)) { tqDebug("WARNING: Unable to open xml for writing!"); return; } // Should we encrypt the data ? TQString content = m_document.toString(); if (KFTPCore::Config::encryptBookmarks()) { DESEncryptor des; if (m_decryptKey.isEmpty()) { // Ask the user for the password KPasswordDialog::getPassword(m_decryptKey, i18n("Enter key for bookmark file encryption.")); } des.setKey(m_decryptKey); des.encrypt(content); content = des.output(); } // Write the XML data to the stream TQTextStream fileStream(&file); fileStream << content; file.flush(); file.close(); } void Manager::validate(TQDomNode node) { if (node.isNull()) node = m_document.documentElement(); TQDomNode n = node.firstChild(); while (!n.isNull()) { if (n.toElement().tagName() == "category") { if (!n.toElement().hasAttribute("id")) n.toElement().setAttribute("id", TQString("cat-%1").arg(TDEApplication::randomString(7))); Manager::validate(n); } else if (n.toElement().tagName() == "server") { if (!n.toElement().hasAttribute("id")) n.toElement().setAttribute("id", TQString("site-%1").arg(TDEApplication::randomString(7))); } n = n.nextSibling(); } } void Manager::versionUpdate() { int version = m_document.documentElement().attribute("version").toInt(); if (version < KFTP_BOOKMARKS_VERSION) { // Conversion from an old bookmark file tqDebug("Detected an old (version %d, new version %d) bookmarks file. Starting conversion process...", version, KFTP_BOOKMARKS_VERSION); // NOTE: There are no breaks here, since every update method updates to a specific // version. So in order to convert to the most current from the oldest version, all // methods need to be called! switch (version) { case 0: case 1: versionFrom1Update(); } // Fix the version m_document.documentElement().setAttribute("version", KFTP_BOOKMARKS_VERSION); } } void Manager::versionFrom1Update(TQDomNode parent) { // The original format had no site ids, so we have to generate them. Also the old // format used something wierd called "options", we have to convert them as well. // The username/password fields now have differend names. if (parent.isNull()) parent = m_document.documentElement(); TQDomNode n = parent.firstChild(); while (!n.isNull()) { if (n.toElement().tagName() == "category") { // Update the category id and all children n.toElement().setAttribute("id", TQString("cat-%1").arg(TDEApplication::randomString(7))); versionFrom1Update(n); } else if (n.toElement().tagName() == "server") { // Update the server id n.toElement().setAttribute("id", TQString("site-%1").arg(TDEApplication::randomString(7))); // Convert the "options" TQDomNodeList nodes = n.toElement().elementsByTagName("option"); if (nodes.length() > 0) { for (unsigned int i = 0; i < nodes.count(); i++) { TQDomNode node = nodes.item(i); // Add a new standard property TQDomElement property = m_document.createElement(node.toElement().attribute("name")); n.appendChild(property); TQDomText text = m_document.createTextNode(node.toElement().attribute("value")); property.appendChild(text); // And remove the option :> node.parentNode().removeChild(node); i--; } } // Rename the username/password fields nodes = n.toElement().elementsByTagName("downuser"); if (nodes.length() > 0) { for (unsigned int i = 0; i < nodes.count(); i++) { TQDomNode node = nodes.item(i); node.toElement().setTagName("username"); } } nodes = n.toElement().elementsByTagName("downpass"); if (nodes.length() > 0) { for (unsigned int i = 0; i < nodes.count(); i++) { TQDomNode node = nodes.item(i); node.toElement().setTagName("password"); } } } n = n.nextSibling(); } } Site *Manager::findSite(const KURL &url) { // Find the node, then see if it is already present in the cache TQDomNode siteElement = findSiteElementByUrl(url); if (!siteElement.isNull()) { // Try to get a cached version Site *site = m_siteCache.find(siteElement.toElement().attribute("id")); if (!site) { site = new Site(siteElement); m_siteCache.insert(siteElement.toElement().attribute("id"), site); } return site; } else { return NULL; } } Site *Manager::findSite(const TQString &id) { if (id.isNull()) return NULL; // Try the cache first Site *site = m_siteCache.find(id); if (!site) { // The site was not found, search in the DOM tree and add it to the // cache if found. TQDomNode siteElement = findSiteElementById(id); if (siteElement.isNull()) { tqDebug("WARNING: Unable to find site with id '%s'!", id.ascii()); return NULL; } site = new Site(siteElement); m_siteCache.insert(id, site); } return site; } TQDomNode Manager::findSiteElementByUrl(const KURL &url, TQDomNode parent) { if (parent.isNull()) parent = m_document.documentElement(); TQDomNode n = parent.firstChild(); while (!n.isNull()) { if (n.toElement().tagName() == "category") { // Check the category TQDomNode site = findSiteElementByUrl(url, n); if (!site.isNull()) return site; } else if (n.toElement().tagName() == "server") { // Check if the server matches Site *tmp = new Site(n); if (tmp->getProperty("host") == url.host() && tmp->getIntProperty("port") == url.port() && tmp->getProperty("username") == url.user() && tmp->getProperty("password") == url.pass()) { delete tmp; return n; } delete tmp; } n = n.nextSibling(); } return TQDomNode(); } TQDomNode Manager::findSiteElementById(const TQString &id, TQDomNode parent) { if (parent.isNull()) parent = m_document.documentElement(); TQDomNode n = parent.firstChild(); while (!n.isNull()) { if (n.toElement().tagName() == "category") { // Check the category TQDomNode site = findSiteElementById(id, n); if (!site.isNull()) return site; } else if (n.toElement().tagName() == "server") { // Check if the server matches if (n.toElement().attribute("id") == id) return n; } n = n.nextSibling(); } return TQDomNode(); } TQDomNode Manager::findCategoryElementById(const TQString &id, TQDomNode parent) { if (id == "root") return m_document.documentElement(); if (parent.isNull()) parent = m_document.documentElement(); TQDomNode n = parent.firstChild(); while (!n.isNull()) { if (n.toElement().tagName() == "category") { if (n.toElement().attribute("id") == id) return n; // Check the category TQDomNode site = findCategoryElementById(id, n); if (!site.isNull()) return site; } n = n.nextSibling(); } return TQDomNode(); } Site *Manager::findCategory(const TQString &id) { // Try the cache first Site *site = m_siteCache.find(id); if (!site) { // The site was not found, search in the DOM tree and add it to the // cache if found. TQDomNode siteElement = findCategoryElementById(id); if (siteElement.isNull()) { tqDebug("WARNING: Unable to find category with id '%s'!", id.ascii()); return NULL; } site = new Site(siteElement); m_siteCache.insert(id, site); } return site; } Site *Manager::addSite(Site *category, TQDomNode node) { if (category) return category->addSite(node); return NULL; } void Manager::delSite(Site *site) { // Remove the node from the DOM tree site->m_element.parentNode().removeChild(site->m_element); // Remove the site from cache and it will be automaticly deleted m_siteCache.remove(site->id()); emit update(); } void Manager::setupClient(Site *site, KFTPEngine::Thread *client) { if (site) { // First activate the correct socket and reset the old flags client->selectSocketForProtocol(KURL(TQString("%1://test/").arg(site->getProperty("protocol")))); client->socket()->initConfig(); int retryTime = site->getIntProperty("retrytime"); int retryCnt = site->getIntProperty("retrycount"); if (retryTime != 0) { client->socket()->setConfig("retry", 1); client->socket()->setConfig("max_retries", retryCnt); client->socket()->setConfig("retry_delay", retryTime); } else { client->socket()->setConfig("retry", 0); } client->socket()->setConfig("keepalive.enabled", site->getIntProperty("doKeepalive")); client->socket()->setConfig("keepalive.timeout", site->getIntProperty("keepaliveTimeout")); client->socket()->setConfig("ssl.use_tls", site->getIntProperty("use_tls")); client->socket()->setConfig("ssl.use_implicit", site->getIntProperty("use_implicit")); client->socket()->setConfig("ssl.prot_mode", site->getProperty("tls_data_mode")); client->socket()->setConfig("feat.pasv", site->getIntProperty("disablePASV") == 1 ? 0 : 1); client->socket()->setConfig("feat.epsv", site->getIntProperty("disableEPSV") == 1 ? 0 : 1); client->socket()->setConfig("pasv.use_site_ip", site->getIntProperty("pasvSiteIp")); client->socket()->setConfig("active.no_force_ip", site->getIntProperty("disableForceIp")); client->socket()->setConfig("stat_listings", site->getIntProperty("statListings")); client->socket()->setConfig("encoding", site->getProperty("encoding")); // Should we use a X509 certificate ? if (site->getIntProperty("use_cert") && site->getProperty("protocol") == "ftp") { // Ask the user for the decryption password TQCString certPass; KPasswordDialog::getPassword(certPass, i18n("Please provide your X509 certificate decryption password.")); static_cast(client->socket())->setSslClientCertificate(KSSLPKCS12::loadCertFile(site->getProperty("tls_cert_path"), certPass)); } } else { // Just reset the client, since we don't know the config client->socket()->initConfig(); } } void Manager::guiPopulateBookmarksTree(KFTPWidgets::Bookmarks::ListView *tree, TQDomNode parent, KFTPWidgets::Bookmarks::ListViewItem *item) { if (parent.isNull()) { // Clear the tree and set the parent tree->clear(); parent = m_document.documentElement(); } TQDomNode n = parent.firstChild(); while (!n.isNull()) { if (n.toElement().tagName() == "category") { // Add a submenu KFTPWidgets::Bookmarks::ListViewItem *cat; Site *site = findCategory(n.toElement().attribute("id")); if (!item) cat = new KFTPWidgets::Bookmarks::ListViewItem(tree, site->getAttribute("name")); else cat = new KFTPWidgets::Bookmarks::ListViewItem(item, site->getAttribute("name")); cat->setType(0); cat->setSite(site); cat->setPixmap(0, loadSmallPixmap("bookmark_folder")); guiPopulateBookmarksTree(tree, n, cat); } else if (n.toElement().tagName() == "server") { KFTPWidgets::Bookmarks::ListViewItem *serv; Site *site = findSite(n.toElement().attribute("id")); if (!item) serv = new KFTPWidgets::Bookmarks::ListViewItem(tree, site->getAttribute("name")); else serv = new KFTPWidgets::Bookmarks::ListViewItem(item, site->getAttribute("name")); serv->setType(1); serv->setSite(site); serv->setPixmap(0, loadSmallPixmap("ftp")); } n = n.nextSibling(); } } void Manager::guiPopulateBookmarksMenu(TDEActionMenu *parentMenu, TQDomNode parentNode, bool base, TQObject *data) { if (parentNode.isNull()) parentNode = m_document.documentElement(); TQDomNode n = parentNode.firstChild(); TDEActionMenu *menu = 0L; KFTPBookmarkAction *action = 0L; while (!n.isNull()) { TQString name = n.toElement().attribute("name"); if (n.toElement().tagName() == "category") { menu = new TDEActionMenu(name, "bookmark_folder", parentMenu); parentMenu->insert(menu, base ? 5 : 0); // Fill the menu guiPopulateBookmarksMenu(menu, n, false, data); } else if (n.toElement().tagName() == "server") { action = new KFTPBookmarkAction(name, "ftp", TDEShortcut(), this, SLOT(slotBookmarkExecuted()), KFTPAPI::getInstance()->mainWindow()->actionCollection()); action->setSiteId(n.toElement().attribute("id")); action->setData(data); parentMenu->insert(action); } n = n.nextSibling(); } } void Manager::guiPopulateZeroconfMenu(TDEActionMenu *parentMenu) { // Clear the menu parentMenu->popupMenu()->clear(); #if KDE_IS_VERSION(3,4,0) // Populate TQValueList list = KFTPAPI::getInstance()->zeroConfInterface()->getServiceList(); if (!list.empty()) { TQValueList::iterator end(list.end()); for (TQValueList::iterator i(list.begin()); i != end; ++i) { KFTPZeroconfAction *newService = new KFTPZeroconfAction((*i)->serviceName(), "lan", TDEShortcut(), this, SLOT(slotZeroconfExecuted()), KFTPAPI::getInstance()->mainWindow()->actionCollection()); newService->setSite(*i); parentMenu->insert(newService); } } else { TDEAction *disabledAction = new TDEAction(i18n("")); disabledAction->setEnabled(false); parentMenu->insert(disabledAction); } #else TDEAction *disabledAction = new TDEAction(i18n("")); disabledAction->setEnabled(false); parentMenu->insert(disabledAction); #endif } void Manager::guiPopulateWalletMenu(TDEActionMenu *parentMenu) { // Clear the menu parentMenu->popupMenu()->clear(); // Populate TQValueList list = KFTPAPI::getInstance()->walletConnection()->getSiteList(); if (!list.empty()) { TQValueList::iterator end(list.end()); for (TQValueList::iterator i(list.begin()); i != end; ++i) { TQString desc; if ((*i).port() != 21) desc = TQString("%1@%2:%3").arg((*i).user()).arg((*i).host()).arg((*i).port()); else desc = TQString("%1@%2").arg((*i).user()).arg((*i).host()); KFTPWalletAction *newSite = new KFTPWalletAction(desc, "ftp", TDEShortcut(), this, SLOT(slotWalletExecuted()), KFTPAPI::getInstance()->mainWindow()->actionCollection()); newSite->setSite(*i); parentMenu->insert(newSite); } } else { TDEAction *disabledAction = new TDEAction(i18n("")); disabledAction->setEnabled(false); parentMenu->insert(disabledAction); } } void Manager::slotBookmarkExecuted() { // Get the sender KFTPBookmarkAction *action = (KFTPBookmarkAction*) TQObject::sender(); Site *site = findSite(action->siteId()); // Get the node data from bookmarks KURL siteUrl = site->getUrl(); // Handle empty usernames and passwords for non-anonymous sites if (!siteUrl.hasUser() || !siteUrl.hasPass() && siteUrl.user() != "anonymous") { TDEIO::PasswordDialog *dlg = new TDEIO::PasswordDialog(i18n("Please provide your username and password for connecting to this site."), siteUrl.user(), true); dlg->addCommentLine(i18n("Site:"), TQString("%1:%2").arg(siteUrl.host()).arg(siteUrl.port())); if (dlg->exec() == KDialogBase::Accepted) { siteUrl.setUser(dlg->username()); siteUrl.setPass(dlg->password()); if (dlg->keepPassword()) { // Save password to the bookmarked site site->setProperty("username", dlg->username()); site->setProperty("password", encodePassword(dlg->password())); } delete dlg; } else { // Abort connection attempt delete dlg; return; } } if (action->data()) { // A specific session was passed on to us KFTPSession::Session *session = static_cast(action->data()); // Set the correct client for connection KFTPEngine::Thread *client = session->getClient(); // Now, connect to the server if (client->socket()->isConnected()) { if (KFTPCore::Config::confirmDisconnects() && KMessageBox::warningYesNo(0, i18n("Do you want to drop current connection?")) == KMessageBox::No) return; } client->socket()->setCurrentUrl(siteUrl); // Set the session's site and connect session->setSite(site); session->reconnect(siteUrl); } else { // Just spawn a new session KFTPSession::Session *session = KFTPSession::Manager::self()->spawnRemoteSession(KFTPSession::IgnoreSide, siteUrl, site); KFTPSession::Manager::self()->setActive(session); } } void Manager::slotWalletExecuted() { // Get the sender KFTPWalletAction *action = (KFTPWalletAction*) TQObject::sender(); KURL siteUrl = action->getSite(); // Just spawn a new session KFTPSession::Manager::self()->spawnRemoteSession(KFTPSession::IgnoreSide, siteUrl); } void Manager::slotZeroconfExecuted() { #if KDE_IS_VERSION(3,4,0) // Get the sender KFTPZeroconfAction *action = (KFTPZeroconfAction*) TQObject::sender(); DNSSD::RemoteService::Ptr service = action->getSite(); KFTPAPI::getInstance()->mainWindow()->slotQuickConnect(service->serviceName(), service->hostName(), service->port()); #endif } } #include "kftpbookmarks.moc"