/*
 * This file is part of the KFTPGrabber project
 *
 * Copyright (C) 2003-2004 by the KFTPGrabber developers
 * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
 *
 * 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/config.h"
#include "engine/thread.h"
#include "engine/ftpsocket.h"

#include <qfile.h>

#include <kdebug.h>
#include <klocale.h>
#include <kiconloader.h>
#include <kpassdlg.h>
#include <kmessagebox.h>
#include <kdeversion.h>
#include <kapplication.h>
#include <kio/passdlg.h>
#include <kstaticdeleter.h>

/* KSSL includes */
#include <ksslpkcs12.h>

using namespace KFTPGrabberBase;

namespace KFTPBookmarks {

Site::Site(QDomNode 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", QString("cat-%1").arg(KApplication::randomString(7)));
  } else if (m_element.tagName() == "server") {
    m_type = ST_SITE;

    if (getAttribute("id").isEmpty())
      setAttribute("id", QString("site-%1").arg(KApplication::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", QString("site-%1").arg(KApplication::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(QDomNode 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 QString &name)
{
  QDomElement cat = m_element.ownerDocument().createElement("category");

  // Assign a new id and name
  cat.setAttribute("id", QString("cat-%1").arg(KApplication::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
  QDomElement parent = m_element.parentNode().toElement();

  if (parent.isNull())
    return NULL;
  else
    return KFTPBookmarks::Manager::self()->findCategory(parent.attribute("id"));
}

QString Site::getProperty(const QString &name)
{
  QDomNodeList nodes = m_element.elementsByTagName(name);

  if (nodes.length() > 0) {
    QString 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 QString::null;
  }
}

int Site::getIntProperty(const QString &name)
{
  return getProperty(name).toInt();
}

void Site::setProperty(const QString &name, const QString &value)
{
  // First delete the old property if it exists
  QDomNodeList nodes = m_element.elementsByTagName(name);

  if (nodes.length() > 0)
    m_element.removeChild(nodes.item(0));

  // Now add a new one
  QDomElement property = m_element.ownerDocument().createElement(name);
  m_element.appendChild(property);

  QDomText text = m_element.ownerDocument().createTextNode(value);
  property.appendChild(text);
}

void Site::setProperty(const QString &name, int value)
{
  setProperty(name, QString::number(value));
}

void Site::setAttribute(const QString &name, const QString &value)
{
  m_element.setAttribute(name, value);
}

QString Site::getAttribute(const QString &name)
{
  return m_element.attribute(name);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

Manager *Manager::m_self = 0;
static KStaticDeleter<Manager> staticBookmarkManagerDeleter;

Manager *Manager::self()
{
  if (!m_self) {
    staticBookmarkManagerDeleter.setObject(m_self, new Manager());
  }
  
  return m_self;
}

Manager::Manager()
  : QObject()
{
  // Init the cache
  m_siteCache.setAutoDelete(true);

  // Init the DOM document
  m_document = QDomDocument("KFTPGrabber");
}

Manager::Manager(const Manager &bookmarks)
  : QObject()
{
  // Init the cache
  m_siteCache.setAutoDelete(true);

  // Init the DOM document
  m_document = QDomDocument("KFTPGrabber");

  // Copy the bookmarks
  QDomNode 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 = QDomDocument("KFTPGrabber");

  QDomNode tmp = m_document.importNode(bookmarks->m_document.documentElement(), true);
  m_document.appendChild(tmp);

  // Clear the cache
  m_siteCache.clear();

  emit update();
}

void Manager::importSites(QDomNode node)
{
  QDomNode import = m_document.importNode(node, true);
  m_document.documentElement().appendChild(import);
  
  // Run sanity checks to generate missing ids
  Manager::validate();
}

void Manager::load(const QString &filename)
{
  m_filename = filename;

  QFile file(filename);
  if (!file.open(IO_ReadOnly)) {
    // Create a new empty XML
    m_document.setContent(QString("<sites version=\"%1\"></sites>").arg(KFTP_BOOKMARKS_VERSION));

    return;
  }

  // Check if the file is encrpyted
  QCString content(file.readAll());
  
  if (KFTPCore::Config::encryptBookmarks()) {
    // File is encrypted
pwd_entry:
    int saveToWallet = 1;

    QCString p_pass(KFTPAPI::getInstance()->walletConnection()->getPassword("bookmarkDecryptPwd").ascii());
    if (QString(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) != "<sites" && des.output().left(9) != "<!DOCTYPE") {
      // Clear any saved passwords
      KFTPAPI::getInstance()->walletConnection()->setPassword("bookmarkDecryptPwd", QString::null);

      if (KMessageBox::warningContinueCancel(
            KFTPAPI::getInstance()->mainWindow(),
            i18n("<qt>Bookmark file decryption has failed with provided key. Do you want to <b>overwrite</b> bookmarks with an empty file ?<br><br><font color=\"red\"><b>Warning:</b> If you overwrite, all current bookmarks will be lost.</font></qt>"),
            i18n("Decryption Failed"),
            KGuiItem(i18n("&Overwrite Bookmarks"))
          ) != KMessageBox::Continue)
      {
        // Request the password again
        goto pwd_entry;
      }

      // Create new empty XML
      m_document.setContent(QString("<sites version=\"%1\"></sites>").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 KWallet
      KFTPAPI::getInstance()->walletConnection()->setPassword("bookmarkDecryptPwd", p_pass);
    }
  }

  m_document.setContent(QString::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()) {
    qDebug("WARNING: No open XML file, will NOT save.");
    return;
  }

  QFile file(m_filename);
  if (!file.open(IO_WriteOnly)) {
    qDebug("WARNING: Unable to open xml for writing!");
    return;
  }

  // Should we encrypt the data ?
  QString 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
  QTextStream fileStream(&file);
  fileStream << content;
  file.flush();
  file.close();
}

void Manager::validate(QDomNode node)
{
  if (node.isNull())
    node = m_document.documentElement();
    
  QDomNode n = node.firstChild();

  while (!n.isNull()) {
    if (n.toElement().tagName() == "category") {
      if (!n.toElement().hasAttribute("id"))
        n.toElement().setAttribute("id", QString("cat-%1").arg(KApplication::randomString(7)));
      
      Manager::validate(n);
    } else if (n.toElement().tagName() == "server") {
      if (!n.toElement().hasAttribute("id"))
        n.toElement().setAttribute("id", QString("site-%1").arg(KApplication::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
    qDebug("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(QDomNode 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();

  QDomNode n = parent.firstChild();

  while (!n.isNull()) {
    if (n.toElement().tagName() == "category") {
      // Update the category id and all children
      n.toElement().setAttribute("id", QString("cat-%1").arg(KApplication::randomString(7)));

      versionFrom1Update(n);
    } else if (n.toElement().tagName() == "server") {
      // Update the server id
      n.toElement().setAttribute("id", QString("site-%1").arg(KApplication::randomString(7)));

      // Convert the "options"
      QDomNodeList nodes = n.toElement().elementsByTagName("option");

      if (nodes.length() > 0) {
        for (unsigned int i = 0; i < nodes.count(); i++) {
          QDomNode node = nodes.item(i);

          // Add a new standard property
          QDomElement property = m_document.createElement(node.toElement().attribute("name"));
          n.appendChild(property);

          QDomText 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++) {
          QDomNode 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++) {
          QDomNode 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
  QDomNode 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 QString &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.
    QDomNode siteElement = findSiteElementById(id);

    if (siteElement.isNull()) {
      qDebug("WARNING: Unable to find site with id '%s'!", id.ascii());

      return NULL;
    }

    site = new Site(siteElement);
    m_siteCache.insert(id, site);
  }

  return site;
}

QDomNode Manager::findSiteElementByUrl(const KURL &url, QDomNode parent)
{
  if (parent.isNull())
    parent = m_document.documentElement();

  QDomNode n = parent.firstChild();

  while (!n.isNull()) {
    if (n.toElement().tagName() == "category") {
      // Check the category
      QDomNode 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 QDomNode();
}

QDomNode Manager::findSiteElementById(const QString &id, QDomNode parent)
{
  if (parent.isNull())
    parent = m_document.documentElement();

  QDomNode n = parent.firstChild();

  while (!n.isNull()) {
    if (n.toElement().tagName() == "category") {
      // Check the category
      QDomNode 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 QDomNode();
}

QDomNode Manager::findCategoryElementById(const QString &id, QDomNode parent)
{
  if (id == "root")
    return m_document.documentElement();

  if (parent.isNull())
    parent = m_document.documentElement();

  QDomNode n = parent.firstChild();

  while (!n.isNull()) {
    if (n.toElement().tagName() == "category") {
      if (n.toElement().attribute("id") == id)
        return n;

      // Check the category
      QDomNode site = findCategoryElementById(id, n);

      if (!site.isNull())
        return site;
    }

    n = n.nextSibling();
  }

  return QDomNode();
}

Site *Manager::findCategory(const QString &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.
    QDomNode siteElement = findCategoryElementById(id);

    if (siteElement.isNull()) {
      qDebug("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, QDomNode 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(QString("%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
      QCString certPass;
      KPasswordDialog::getPassword(certPass, i18n("Please provide your X509 certificate decryption password."));

      static_cast<KFTPEngine::FtpSocket*>(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, QDomNode parent, KFTPWidgets::Bookmarks::ListViewItem *item)
{
  if (parent.isNull()) {
    // Clear the tree and set the parent
    tree->clear();
    parent = m_document.documentElement();
  }

  QDomNode 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(KActionMenu *parentMenu, QDomNode parentNode, bool base, QObject *data)
{
  if (parentNode.isNull())
    parentNode = m_document.documentElement();

  QDomNode n = parentNode.firstChild();
  KActionMenu *menu = 0L;
  KFTPBookmarkAction *action = 0L;

  while (!n.isNull()) {
    QString name = n.toElement().attribute("name");

    if (n.toElement().tagName() == "category") {
      menu = new KActionMenu(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", KShortcut(), 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(KActionMenu *parentMenu)
{
  // Clear the menu
  parentMenu->popupMenu()->clear();

#if KDE_IS_VERSION(3,4,0)
  // Populate
  QValueList<DNSSD::RemoteService::Ptr> list = KFTPAPI::getInstance()->zeroConfInterface()->getServiceList();

  if (!list.empty()) {
    QValueList<DNSSD::RemoteService::Ptr>::iterator end(list.end());

    for (QValueList<DNSSD::RemoteService::Ptr>::iterator i(list.begin()); i != end; ++i) {
      KFTPZeroconfAction *newService = new KFTPZeroconfAction((*i)->serviceName(), "lan", KShortcut(), this, SLOT(slotZeroconfExecuted()), KFTPAPI::getInstance()->mainWindow()->actionCollection());
      newService->setSite(*i);

      parentMenu->insert(newService);
    }
  } else {
    KAction *disabledAction = new KAction(i18n("<No Services Published>"));
    disabledAction->setEnabled(false);
    parentMenu->insert(disabledAction);
  }
#else
  KAction *disabledAction = new KAction(i18n("<DNSSD Not Available>"));
  disabledAction->setEnabled(false);
  parentMenu->insert(disabledAction);
#endif
}

void Manager::guiPopulateWalletMenu(KActionMenu *parentMenu)
{
  // Clear the menu
  parentMenu->popupMenu()->clear();
  
  // Populate
  QValueList<KURL> list = KFTPAPI::getInstance()->walletConnection()->getSiteList();

  if (!list.empty()) {
    QValueList<KURL>::iterator end(list.end());

    for (QValueList<KURL>::iterator i(list.begin()); i != end; ++i) {
      QString desc;
      
      if ((*i).port() != 21)
        desc = QString("%1@%2:%3").arg((*i).user()).arg((*i).host()).arg((*i).port());
      else
        desc = QString("%1@%2").arg((*i).user()).arg((*i).host());
        
      KFTPWalletAction *newSite = new KFTPWalletAction(desc, "ftp", KShortcut(), this, SLOT(slotWalletExecuted()), KFTPAPI::getInstance()->mainWindow()->actionCollection());
      newSite->setSite(*i);

      parentMenu->insert(newSite);
    }
  } else {
    KAction *disabledAction = new KAction(i18n("<No Sites In KWallet>"));
    disabledAction->setEnabled(false);
    parentMenu->insert(disabledAction);
  }
}

void Manager::slotBookmarkExecuted()
{
  // Get the sender
  KFTPBookmarkAction *action = (KFTPBookmarkAction*) QObject::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") {
    KIO::PasswordDialog *dlg = new KIO::PasswordDialog(i18n("Please provide your username and password for connecting to this site."), siteUrl.user(), true);
    dlg->addCommentLine(i18n("Site:"), QString("%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<KFTPSession::Session*>(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*) QObject::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*) QObject::sender();
  DNSSD::RemoteService::Ptr service = action->getSite();

  KFTPAPI::getInstance()->mainWindow()->slotQuickConnect(service->serviceName(), service->hostName(), service->port());
#endif
}

}


#include "kftpbookmarks.moc"
