/****************************************************************************
** Dooble - The Secure Internet Web Browser
**
** Copyright (c) 2008, 2009, 2010, 2011, 2012 Alexis Megas,
** Gunther van Dooble, and the Dooble Team.
** All rights reserved.
**
** License: GPL2 only:
** 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; version 2 of the License only.
**
** 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
** or see here: http://www.gnu.org/licenses/gpl.html
**
** For the WebKit library, please see: http://webkit.org.
**
** THE CODE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY
** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
** IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS APPLICATION, EVEN IF ADVISED
** OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Please report all praise, requests, bugs, and problems to the project
** team and administrators: http://sf.net/projects/dooble.
**
** You can find us listed at our project page. New team members are welcome.
** The name of the authors should not be used to endorse or promote products
** derived from Dooble without specific prior written permission.
** If you use this code for other projects, please let us know.
**
** Web sites:
**   http://sf.net/projects/dooble
**   http://dooble.sf.net
****************************************************************************/

#include <QDir>
#include <QBuffer>
#include <QtEndian>
#include <QProcess>
#include <QSettings>
#include <QSqlQuery>
#include <QProgressBar>
#include <QSqlDatabase>
#include <QtCore/qmath.h>
#include <QDesktopWidget>
#include <QFileIconProvider>
#include <QCryptographicHash>

#include "dmisc.h"
#include "dooble.h"
#include "derrorlog.h"

/*
** OK, so you know. One of the below proxy methods (a static one)
** returns a list of proxies. How does one detect which proxy is valid?
*/

/*
** The method systemProxyForQuery() may take several
** seconds to execute on Windows systems. Therefore,
** some intelligence may need to be introduced in order to
** enhance Dooble's response times.
*/

extern "C"
{
#include <errno.h>

#ifdef _PTHREAD_H
  GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
}

#ifndef _PTHREAD_H
extern "C"
{
  int gcry_qthread_init(void)
  {
    return 0;
  }

  int gcry_qmutex_init(void **mutex)
  {
    *mutex = (void *) (new QMutex());

    if(*mutex)
      return 0;
    else
      return -1;
  }

  int gcry_qmutex_destroy(void **mutex)
  {
    delete (QMutex *) *mutex;
    return 0;
  }

  int gcry_qmutex_lock(void **mutex)
  {
    QMutex *m = (QMutex *) *mutex;

    if(m)
      {
	m->lock();
	return 0;
      }
    else
      return -1;
  }

  int gcry_qmutex_unlock(void **mutex)
  {
    QMutex *m = (QMutex *) *mutex;

    if(m)
      {
	m->unlock();
	return 0;
      }
    else
      return -1;
  }
}

struct gcry_thread_cbs gcry_threads_qt =
  {
    GCRY_THREAD_OPTION_USER, gcry_qthread_init, gcry_qmutex_init,
    gcry_qmutex_destroy, gcry_qmutex_lock, gcry_qmutex_unlock,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
  };
#endif

int dmisc::s_algorithm = -1;
bool dmisc::s_cryptOK = true;
bool dmisc::s_passphraseWasAuthenticated = false;
char *dmisc::s_passphraseHash = 0;
size_t dmisc::s_passphraseHashLength = 0;
QMutex dmisc::s_cipherMutex;
QString dmisc::s_hashType;
gcry_cipher_hd_t dmisc::s_cipherCtx;
QList<QPair<QUrl, QIcon> > dmisc::s_faviconsList;

void dmisc::destroyCrypt(void)
{
  gcry_cipher_close(s_cipherCtx);
}

void dmisc::initializeCrypt(void)
{
#ifdef _PTHREAD_H
  gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread, 0);
#else
  logError(QObject::tr("dmisc::initializeCrypt(): Using gcry_threads_qt's "
		       "address as the second parameter to gcry_control()."));
  gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_qt, 0);
#endif

  if(!gcry_check_version(GCRYPT_VERSION))
    {
      s_cryptOK = false;
      logError(QObject::tr("dmisc::initializeCrypt(): "
			   "gcry_check_version() failure."));
    }
  else
    {
      /*
      ** Disable secure memory.
      */

      gcry_control(GCRYCTL_ENABLE_M_GUARD);
      gcry_control(GCRYCTL_SUSPEND_SECMEM_WARN);

      if(gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0) != 0)
	s_cryptOK = false;

      gcry_control(GCRYCTL_RESUME_SECMEM_WARN);
      gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);

      if(s_cryptOK)
	{
	  /*
	  ** Remember, initializeCrypt() is called before
	  ** a QApplication object is created. As a result,
	  ** a QSettings object may not be in the correct
	  ** context.
	  */

	  int algorithm = gcry_cipher_map_name
	    (findCipherType().toAscii().constData());
	  gcry_error_t err = 0;

	  if((err = gcry_cipher_open(&s_cipherCtx, algorithm,
				     GCRY_CIPHER_MODE_CBC,
				     GCRY_CIPHER_SECURE |
				     GCRY_CIPHER_CBC_CTS)) != 0)
	    {
	      s_cryptOK = false;
	      logError(QObject::tr("dmisc::initializeCrypt(): "
				   "gcry_cipher_open() failure (%1).").
		       arg(gcry_strerror(err)));
	    }
	  else
	    {
	      s_algorithm = algorithm;
	      s_hashType = findHashType();
	    }
	}
    }
}

void dmisc::setCipherPassphrase(const QString &passphrase, const bool save)
{
  if(s_cryptOK)
    {
      bool isRandomPassphrase = true;
      char *l_passphrase = 0;
      size_t passphraseLength = 0;
      gcry_error_t err = 0;

      if(passphrase.isEmpty())
	{
	  passphraseLength = 256;
	  l_passphrase = (char *) gcry_calloc_secure
	    (passphraseLength, sizeof(char));
	}
      else
	{
	  isRandomPassphrase = false;
	  passphraseLength = passphrase.length();
	  l_passphrase = (char *) gcry_calloc_secure
	    (passphraseLength, sizeof(char));
	}

      if(!l_passphrase)
	{
	  s_cryptOK = false;
	  s_passphraseWasAuthenticated = false;
	  return;
	}

      if(passphrase.isEmpty())
	gcry_create_nonce((void *) l_passphrase, passphraseLength);
      else
	{
	  memcpy((void *) l_passphrase,
		 (const void *) passphrase.toAscii().constData(),
		 (size_t) passphrase.toAscii().length());
	  s_passphraseWasAuthenticated = true;
	}

      if(save && s_passphraseWasAuthenticated)
	{
	  /*
	  ** The setKey() method may derive a key based on some of the
	  ** following information. Therefore, we need to update the
	  ** information before calling setKey().
	  */

	  QString hash("");
	  QString hashType(s_hashType);
	  QByteArray salt(256, 0);

	  gcry_create_nonce((void *) salt.data(), salt.length());
	  hash = passphraseHash(passphrase, salt.toHex(), hashType);

	  QSettings settings;

	  settings.setValue("settingsWindow/passphraseHash", hash);
	  settings.setValue("settingsWindow/passphraseHashType", hashType);
	  settings.setValue("settingsWindow/passphraseSalt", salt.toHex());
	  dooble::s_settings["settingsWindow/passphraseHash"] = hash;
	  dooble::s_settings["settingsWindow/passphraseHashType"] = hashType;
	  dooble::s_settings["settingsWindow/passphraseSalt"] = salt.toHex();
	}

      if((err = setKey(l_passphrase, passphraseLength,
		       isRandomPassphrase)) != 0)
	{
	  s_algorithm = -1;
	  s_cryptOK = false;
	  s_hashType.clear();
	  s_passphraseWasAuthenticated = false;
	  gcry_free(s_passphraseHash);
	  s_passphraseHash = 0;
	  s_passphraseHashLength = 0;
	  logError
	    (QObject::tr("dmisc::initializeCrypt(): setKey() failure (%1).").
	     arg(gcry_strerror(err)));
	}

      gcry_free(l_passphrase);
    }
}

QNetworkProxy dmisc::proxyByUrl(const QUrl &url)
{
  QString str(dooble::s_settings.value("settingsWindow/browsingProxySetting",
				       "none").toString());
  QString scheme(url.scheme().toLower().trimmed());
  QNetworkProxy proxy;

  proxy.setType(QNetworkProxy::NoProxy);

  if(str == "system")
    {
      QNetworkProxyQuery query(url);
      QList<QNetworkProxy> list
	(QNetworkProxyFactory::systemProxyForQuery(query));

      if(!list.isEmpty())
	proxy = list.at(0);
    }
  else if(str == "manual")
    {
      if(scheme == "ftp")
	if(dooble::s_settings.value("settingsWindow/ftpBrowsingProxyEnabled",
				    false).toBool())
	  {
	    proxy.setType(QNetworkProxy::Socks5Proxy);
	    proxy.setHostName
	      (dooble::s_settings.value("settingsWindow/"
					"ftpBrowsingProxyHost",
					"").toString().trimmed());
	    proxy.setPort
	      (static_cast<quint16> (dooble::s_settings.value
				     ("settingsWindow/ftpBrowsingProxyPort",
				      1080).toInt()));
	    proxy.setUser
	      (dooble::s_settings.value("settingsWindow/"
					"ftpBrowsingProxyUser",
					"").toString());
	    proxy.setPassword
	      (dooble::s_settings.value("settingsWindow/"
					"ftpBrowsingProxyPassword",
					"").toString());
	  }

      if(scheme == "http" || scheme == "https")
	if(dooble::s_settings.value("settingsWindow/httpBrowsingProxyEnabled",
				    false).toBool())
	  {
	    QString str("");

	    str = dooble::s_settings.value
	      ("settingsWindow/httpBrowsingProxyType",
	       "Socks5").toString().trimmed().toLower();

	    if(str == "http")
	      proxy.setType(QNetworkProxy::HttpProxy);
	    else
	      proxy.setType(QNetworkProxy::Socks5Proxy);

	    proxy.setHostName
	      (dooble::s_settings.value("settingsWindow/httpBrowsingProxyHost",
					"").toString().trimmed());
	    proxy.setPort
	      (static_cast<quint16> (dooble::s_settings.value
				     ("settingsWindow/httpBrowsingProxyPort",
				      1080).toInt()));
	    proxy.setUser
	      (dooble::s_settings.value("settingsWindow/httpBrowsingProxyUser",
					"").toString());
	    proxy.setPassword(dooble::s_settings.value
			      ("settingsWindow/httpBrowsingProxyPassword",
			       "").toString());
	  }
    }

  /*
  ** Override!
  */

  if(url.host().toLower().trimmed().endsWith(".i2p") &&
     dooble::s_settings.value("settingsWindow/i2pBrowsingProxyEnabled",
			      true).toBool())
    {
      QString str("");

      str = dooble::s_settings.value("settingsWindow/i2pBrowsingProxyType",
				     "Http").toString().trimmed().
	toLower();

      if(str == "socks5")
	proxy.setType(QNetworkProxy::Socks5Proxy);
      else if(str == "http")
	proxy.setType(QNetworkProxy::HttpProxy);
      else
	proxy.setType(QNetworkProxy::HttpProxy);

      proxy.setHostName(dooble::s_settings.value
			("settingsWindow/i2pBrowsingProxyHost",
			 "127.0.0.1").toString().trimmed());
      proxy.setPort
	(static_cast<quint16> (dooble::s_settings.value
			       ("settingsWindow/i2pBrowsingProxyPort",
				4444).toInt()));
    }

  return proxy;
}

QNetworkProxy dmisc::proxyByFunctionAndUrl
(const DoobleDownloadType::DoobleDownloadTypeEnum functionType,
 const QUrl &url)
{
  QString str(dooble::s_settings.value("settingsWindow/browsingProxySetting",
				       "none").toString());
  QNetworkProxy proxy;

  proxy.setType(QNetworkProxy::NoProxy);

  switch(functionType)
    {
    case DoobleDownloadType::Ftp:
      {
	if(str == "system")
	  {
	    QNetworkProxyQuery query(url);
	    QList<QNetworkProxy> list
	      (QNetworkProxyFactory::systemProxyForQuery(query));

	    if(!list.isEmpty())
	      proxy = list.at(0);
	  }
	else if(str == "manual" &&
		dooble::s_settings.value
		("settingsWindow/ftpDownloadProxyEnabled",
		 false).toBool())
	  {
	    proxy.setType(QNetworkProxy::Socks5Proxy);
	    proxy.setHostName(dooble::s_settings.value("settingsWindow/"
						       "ftpDownloadProxyHost",
						       "").toString().
			      trimmed());
	    proxy.setPort
	      (static_cast<quint16> (dooble::s_settings.value
				     ("settingsWindow/ftpDownloadProxyPort",
				      1080).toInt()));
	    proxy.setUser(dooble::s_settings.value("settingsWindow/"
						   "ftpDownloadProxyUser",
						   "").toString());
	    proxy.setPassword
	      (dooble::s_settings.value("settingsWindow/"
					"ftpDownloadProxyPassword",
					"").toString());
	  }

	break;
      }
    case DoobleDownloadType::Http:
      {
	if(str == "system")
	  {
	    QNetworkProxyQuery query(url);
	    QList<QNetworkProxy> list
	      (QNetworkProxyFactory::systemProxyForQuery(query));

	    if(!list.isEmpty())
	      proxy = list.at(0);
	  }
	else if(str == "manual" &&
		dooble::s_settings.value
		("settingsWindow/httpDownloadProxyEnabled",
		 false).toBool())
	  {
	    QString str("");

	    str = dooble::s_settings.value
	      ("settingsWindow/httpDownloadProxyType",
	       "Socks5").toString().trimmed().
	      toLower();

	    if(str == "http")
	      proxy.setType(QNetworkProxy::HttpProxy);
	    else
	      proxy.setType(QNetworkProxy::Socks5Proxy);

	    proxy.setHostName(dooble::s_settings.value
			      ("settingsWindow/httpDownloadProxyHost",
			       "").toString().trimmed());
	    proxy.setPort
	      (static_cast<quint16> (dooble::s_settings.value
				     ("settingsWindow/httpDownloadProxyPort",
				      1080).toInt()));
	    proxy.setUser(dooble::s_settings.value
			  ("settingsWindow/httpDownloadProxyUser",
			   "").toString());
	    proxy.setPassword
	      (dooble::s_settings.value
	       ("settingsWindow/httpDownloadProxyPassword",
		"").toString());
	  }

	break;
      }
    default:
      break;
    }

  /*
  ** Override!
  */

  if(url.host().toLower().trimmed().endsWith(".i2p") &&
     dooble::s_settings.value("settingsWindow/i2pDownloadProxyEnabled",
			      true).toBool())
    {
      QString str("");

      str = dooble::s_settings.value("settingsWindow/i2pDownloadProxyType",
				     "Http").toString().trimmed().
	toLower();

      if(str == "socks5")
	proxy.setType(QNetworkProxy::Socks5Proxy);
      else if(str == "http")
	proxy.setType(QNetworkProxy::HttpProxy);
      else
	proxy.setType(QNetworkProxy::HttpProxy);

      proxy.setHostName(dooble::s_settings.value
			("settingsWindow/i2pDownloadProxyHost",
			 "127.0.0.1").toString().trimmed());
      proxy.setPort
	(static_cast<quint16> (dooble::s_settings.value
			       ("settingsWindow/i2pDownloadProxyPort",
				4444).toInt()));
    }

  return proxy;
}

void dmisc::saveIconForUrl(const QIcon &icon, const QUrl &url)
{
  if(!dooble::s_settings.value("settingsWindow/enableFaviconsDatabase",
			      false).toBool())
    return;
  else if(icon.isNull() || url.isEmpty() || !url.isValid())
    return;

  {
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "favicons");

    db.setDatabaseName(dooble::s_homePath + QDir::separator() +
		       "favicons.db");

    if(db.open())
      {
	int temporary = passphraseWasAuthenticated() ? 0 : 1;
	QSqlQuery query(db);

	query.exec("CREATE TABLE IF NOT EXISTS favicons ("
		   "url TEXT NOT NULL, "
		   "url_hash TEXT NOT NULL, "
		   "favicon BLOB DEFAULT NULL, "
		   "temporary INTEGER NOT NULL, "
		   "PRIMARY KEY (url_hash, temporary))");
	query.exec("PRAGMA synchronous = OFF");
	query.prepare
	  ("INSERT OR REPLACE INTO favicons "
	   "(url, url_hash, favicon, temporary) "
	   "VALUES (?, ?, ?, ?)");
	query.bindValue
	  (0,
	   encodedString(url.toEncoded(QUrl::StripTrailingSlash),
			 true).toBase64());
	query.bindValue
	  (1,
	   hashedString(url.toEncoded(QUrl::StripTrailingSlash)).toBase64());

	QBuffer buffer;
	QByteArray bytes;

	buffer.setBuffer(&bytes);
	buffer.open(QIODevice::WriteOnly);

	QDataStream out(&buffer);

	if(url.scheme().toLower().trimmed() == "ftp")
	  out << icon.pixmap(16, QIcon::Normal, QIcon::On);
	else
	  out << icon;

	query.bindValue
	  (2,
	   encodedString(bytes, true).toBase64());
	buffer.close();
	query.bindValue(3, temporary);
	query.exec();
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("favicons");
}

QIcon dmisc::iconForUrl(const QUrl &url)
{
  QString scheme(url.scheme().toLower().trimmed());
  QFileIconProvider iconProvider;

  if(scheme == "ftp" || scheme == "http" || scheme == "https" ||
     scheme == "qrc")
    {
      QIcon icon;
      QPixmap pixmap;

      {
	QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE",
						    "favicons");

	db.setDatabaseName(dooble::s_homePath +
			   QDir::separator() + "favicons.db");

	if(db.open())
	  {
	    int temporary = passphraseWasAuthenticated() ? 0 : 1;
	    QSqlQuery query(db);

	    query.prepare("SELECT favicon FROM favicons WHERE "
			  "temporary = ? AND "
			  "url_hash = ?");
	    query.bindValue(0, temporary);
	    query.bindValue
	      (1, hashedString(url.toEncoded(QUrl::StripTrailingSlash)).
	       toBase64());

	    if(query.exec())
	      if(query.next())
		if(!query.isNull(0))
		  {
		    QBuffer buffer;
		    QByteArray bytes
		      (QByteArray::fromBase64(query.value(0).
					      toByteArray()));

		    bytes = decodedString(bytes);
		    buffer.setBuffer(&bytes);
		    buffer.open(QIODevice::ReadOnly);

		    QDataStream in(&buffer);

		    if(scheme == "ftp")
		      in >> pixmap;
		    else
		      in >> icon;

		    buffer.close();
		  }
	  }

	db.close();
      }

      QSqlDatabase::removeDatabase("favicons");

      if(scheme == "ftp")
	icon = QIcon(pixmap);

      if(!icon.isNull())
	return icon;
    }
  else if(scheme == "file")
    {
      QFileInfo fileInfo(url.toLocalFile());

      if(fileInfo.isFile())
	return iconProvider.icon(QFileIconProvider::File);
      else if(fileInfo.isDir())
	return iconProvider.icon(QFileIconProvider::Folder);
    }

  QSettings settings
    (dooble::s_settings.value("iconSet").toString(), QSettings::IniFormat);

  return QIcon(settings.value("mainWindow/emptyIcon").toString());
}

QByteArray dmisc::decodedString(const QByteArray &byteArray)
{
  QMutexLocker locker(&s_cipherMutex);

  if(s_cryptOK)
    {
      QByteArray encodedArray(byteArray);
      gcry_error_t err = 0;

      gcry_cipher_reset(s_cipherCtx);

      if((err = setInitializationVector(encodedArray)) != 0)
      	{
	  logError(QObject::tr("dmisc::decodedString(): "
			       "setInitializationVector() failure (%1).").
		   arg(gcry_strerror(err)));
	  return byteArray;
	}
      else
	{
	  size_t blockLength = gcry_cipher_get_algo_blklen(s_algorithm);

	  if(blockLength == 0)
	    {
	      logError(QObject::tr("dmisc::decodedString(): "
				   "gcry_cipher_get_algo_blklen() returned "
				   "zero."));
	      return byteArray;
	    }

	  QByteArray decodedArray(encodedArray);

	  /*
	  ** Block ciphers require the length of the buffers
	  ** to be multiples of the cipher's block size.
	  */

	  if(decodedArray.isEmpty())
	    decodedArray = decodedArray.leftJustified(blockLength, 0);
	  else
	    decodedArray = decodedArray.leftJustified
	      (blockLength * qCeil((qreal) decodedArray.length() / (qreal)
				   blockLength), 0);

	  if((err = gcry_cipher_decrypt(s_cipherCtx,
					(void *) decodedArray.constData(),
					(size_t) decodedArray.length(),
					(const void *) 0,
					(size_t) 0)) == 0)
	    {
	      int s = 0;

	      if(!decodedArray.isEmpty())
		s = qAbs(decodedArray.at(decodedArray.length() - 1));

	      if(s <= decodedArray.length())
		return decodedArray.mid(0, decodedArray.length() - s);
	      else
		return decodedArray;
	    }
	  else
	    {
	      logError(QObject::tr("dmisc::decodedString(): "
				   "gcry_cipher_decrypt() failure (%1).").
		       arg(gcry_strerror(err)));
	      return byteArray;
	    }
	}
    }
  else
    return byteArray;
}

QByteArray dmisc::encodedString(const QByteArray &byteArray,
				const bool shouldEncode)
{
  QMutexLocker locker(&s_cipherMutex);

  if(s_cryptOK && shouldEncode)
    {
      QByteArray iv;
      gcry_error_t err = 0;

      gcry_cipher_reset(s_cipherCtx);

      if((err = setInitializationVector(iv)) != 0)
	{
	  logError(QObject::tr("dmisc::encodedString(): "
			       "setInitializationVector() failure (%1).").
		   arg(gcry_strerror(err)));
	  return byteArray;
	}
      else
	{
	  size_t blockLength = gcry_cipher_get_algo_blklen(s_algorithm);

	  if(blockLength == 0)
	    {
	      logError(QObject::tr("dmisc::encodedString(): "
				   "gcry_cipher_get_algo_blklen() returned "
				   "zero."));
	      return byteArray;
	    }

	  QByteArray encodedArray(byteArray);

	  /*
	  ** Block ciphers require the length of the buffers
	  ** to be multiples of the cipher's block size.
	  */

	  if(encodedArray.isEmpty())
	    encodedArray = encodedArray.leftJustified(blockLength, 0);
	  else
	    encodedArray = encodedArray.leftJustified
	      (blockLength * qCeil((qreal) encodedArray.length() / (qreal)
				   blockLength), 0);

	  encodedArray.append(QByteArray(blockLength, 0));

	  char byte = 0xFF & (encodedArray.length() - byteArray.length());

	  encodedArray.remove(encodedArray.length() - 1, 1);
	  encodedArray.append(byte);

	  if((err = gcry_cipher_encrypt(s_cipherCtx,
					(void *) encodedArray.constData(),
					(size_t) encodedArray.length(),
					(const void *) 0,
					(size_t) 0)) == 0)
	    return iv + encodedArray;
	  else
	    {
	      logError(QObject::tr("dmisc::encodedString(): "
				   "gcry_cipher_encrypt() failure (%1).").
		       arg(gcry_strerror(err)));
	      return byteArray;
	    }
	}
    }
  else
    return byteArray;
}

QString dmisc::findHashType(void)
{
  QString type("");
  QStringList types;

  types << "sha512"
	<< "sha384"
	<< "sha256"
	<< "sha224"
	<< "tiger";

  for(int i = 0; i < types.size(); i++)
    if(isHashTypeSupported(types.at(i)))
      {
	type = types.at(i);
	break;
      }

  return type;
}

QString dmisc::findCipherType(void)
{
  QString type("");
  QStringList types;

  /*
  ** Block ciphers only!
  */

  types << "aes256"
	<< "camellia256"
	<< "serpent256"
	<< "twofish"
	<< "aes192"
	<< "aes128";

  for(int i = 0; i < types.size(); i++)
    if(isCipherTypeSupported(types.at(i)))
      {
	type = types.at(i);
	break;
      }

  return type;
}

QString dmisc::passphraseHash(const QString &passphrase,
			      const QString &salt,
			      const QString &hashType)
{
  QString hash("");
  QString saltedPassphrase("");

  saltedPassphrase.append(passphrase).append(salt);

  if(s_cryptOK)
    {
      int algorithm = -1;

      if(s_hashType == "sha512")
	algorithm = GCRY_MD_SHA512;
      else if(s_hashType == "sha384")
	algorithm = GCRY_MD_SHA384;
      else if(s_hashType == "sha256")
	algorithm = GCRY_MD_SHA256;
      else if(s_hashType == "sha224")
	algorithm = GCRY_MD_SHA224;
      else if(s_hashType == "tiger")
	algorithm = GCRY_MD_TIGER;

      if(algorithm != -1 && isHashTypeSupported(hashType))
	{
	  QByteArray byteArray(gcry_md_get_algo_dlen(algorithm), 0);

	  gcry_md_hash_buffer
	    (algorithm,
	     (void *) byteArray.data(),
	     (const void *) saltedPassphrase.toUtf8().constData(),
	     (size_t) saltedPassphrase.toUtf8().length());
	  hash = byteArray.toHex();
	}
      else
	hash = QCryptographicHash::hash
	  (saltedPassphrase.toUtf8(), QCryptographicHash::Sha1).toHex();
    }
  else
    hash = QCryptographicHash::hash(saltedPassphrase.toUtf8(),
				    QCryptographicHash::Sha1).toHex();

  return hash;
}

bool dmisc::isHashTypeSupported(const QString &hashType)
{
  if(s_cryptOK)
    {
      int algorithm = gcry_md_map_name(hashType.toAscii().constData());

      if(algorithm != 0 && gcry_md_test_algo(algorithm) == 0)
	return true;
      else
	return false;
    }
  else
    return false;
}

bool dmisc::isCipherTypeSupported(const QString &cipherType)
{
  if(s_cryptOK)
    {
      if(gcry_cipher_map_name(cipherType.toAscii().constData()) != 0)
	return true;
      else
	return false;
    }
  else
    return false;
}

gcry_error_t dmisc::setKey(char *passphrase, const size_t passphraseLength,
			   const bool isRandomPassphrase)
{
  gcry_error_t err = 0;

  if(s_cryptOK && passphrase)
    {
#if DOOBLE_MINIMUM_GCRYPT_VERSION >= 0x010500
      char *key = 0;
      size_t keyLength = gcry_cipher_get_algo_keylen(s_algorithm);

      if(keyLength == 0)
	{
	  err = GPG_ERR_INV_KEYLEN;
	  logError
	    (QObject::tr("dmisc::setKey(): gcry_cipher_get_algo_keylen() "
			 "returned zero."));
	}
      else if((key = (char *) gcry_calloc_secure(keyLength, sizeof(char))))
	{
	  QString hashType("");
	  QByteArray salt;

	  if(isRandomPassphrase)
	    {
	      salt.resize(256);
	      gcry_create_nonce((void *) salt.data(), salt.length());
	      hashType = s_hashType;
	    }
	  else
	    {
	      salt = dooble::s_settings.
		value("settingsWindow/passphraseSalt", "").toByteArray();
	      hashType = dooble::s_settings.
		value("settingsWindow/passphraseHashType", "").toString();
	    }

	  int algorithm = -1;

	  if(hashType == "sha512")
	    algorithm = GCRY_MD_SHA512;
	  else if(hashType == "sha384")
	    algorithm = GCRY_MD_SHA384;
	  else if(hashType == "sha256")
	    algorithm = GCRY_MD_SHA256;
	  else if(hashType == "sha224")
	    algorithm = GCRY_MD_SHA224;
	  else if(hashType == "tiger")
	    algorithm = GCRY_MD_TIGER;
	  else
	    algorithm = GCRY_MD_SHA1;

	  int iterationCount =
	    qMax(1000, dooble::s_settings.
		 value("settingsWindow/iterationCount", 10000).toInt());

	  if((err = gcry_kdf_derive
	      ((const void *) passphrase,
	       passphraseLength,
	       GCRY_KDF_PBKDF2,
	       algorithm,
	       salt.constData(),
	       static_cast<size_t> (salt.length()),
	       iterationCount,
	       keyLength,
	       (void *) key)) == 0)
	    {
	      QByteArray byteArray(gcry_md_get_algo_dlen(algorithm), 0);

	      gcry_md_hash_buffer
		(algorithm,
		 (void *) byteArray.data(),
		 (const void *) key,
		 (size_t) keyLength);
	      s_passphraseHash = (char *) gcry_calloc_secure
		(byteArray.toHex().length(), sizeof(char));
	      s_passphraseHashLength = byteArray.toHex().length();

	      if(s_passphraseHash)
		memcpy((void *) s_passphraseHash,
		       (const void *) byteArray.toHex().constData(),
		       (size_t) byteArray.toHex().length());
	      else
		logError
		  (QObject::tr("dmisc::setKey(): s_passphraseHash is "
			       "0."));

	      err = gcry_cipher_setkey
		(s_cipherCtx, (const void *) key, keyLength);
	    }

	  gcry_free(key);
	}
      else
	err = GPG_ERR_ENOMEM;
#else
      Q_UNUSED(isRandomPassphrase);

      /*
      ** Retain the passphrase's hash. We'll use the hash as a key.
      */

      if(s_passphraseHash)
	gcry_free(s_passphraseHash);

      int algorithm = -1;

      if(s_hashType == "sha512")
	algorithm = GCRY_MD_SHA512;
      else if(s_hashType == "sha384")
	algorithm = GCRY_MD_SHA384;
      else if(s_hashType == "sha256")
	algorithm = GCRY_MD_SHA256;
      else if(s_hashType == "sha224")
	algorithm = GCRY_MD_SHA224;
      else if(s_hashType == "tiger")
	algorithm = GCRY_MD_TIGER;
      else
	algorithm = GCRY_MD_SHA1;

      QByteArray byteArray(gcry_md_get_algo_dlen(algorithm), 0);

      gcry_md_hash_buffer
	(algorithm,
	 (void *) byteArray.data(),
	 (const void *) passphrase,
	 (size_t) passphraseLength);
      s_passphraseHash = (char *) gcry_calloc_secure
	(byteArray.toHex().length(), sizeof(char));
      s_passphraseHashLength = byteArray.toHex().length();

      if(s_passphraseHash)
	memcpy((void *) s_passphraseHash,
	       (const void *) byteArray.toHex().constData(),
	       (size_t) byteArray.toHex().length());
      else
	logError
	  (QObject::tr("dmisc::setKey(): s_passphraseHash is "
		       "0."));

      logError
	(QObject::tr("dmisc::setKey(): gcry_kdf_derive() is not defined. "
		     "Using the provided passphrase as the key."));

      size_t keyLength = gcry_cipher_get_algo_keylen(s_algorithm);

      if(keyLength != 0)
	err = gcry_cipher_setkey
	  (s_cipherCtx, (const void *) passphrase, keyLength);
      else
	{
	  err = GPG_ERR_INV_KEYLEN;
	  logError
	    (QObject::tr("dmisc::setKey(): gcry_cipher_get_algo_keylen() "
			 "returned zero."));
	}
#endif
    }
  else if(!passphrase)
    err = GPG_ERR_ENOMEM;

  return err;
}

gcry_error_t dmisc::setInitializationVector(QByteArray &bytes)
{
  /*
  ** Do you need to lock a mutex?
  */

  char *iv = 0;
  size_t ivLength = 0;
  gcry_error_t err = 0;

  if((ivLength = gcry_cipher_get_algo_blklen(s_algorithm)) == 0)
    err = GPG_ERR_INV_LENGTH;
  else
    {
      iv = (char *) gcry_calloc_secure(ivLength, sizeof(char));

      if(iv)
	{
	  if(bytes.isEmpty())
	    {
	      gcry_create_nonce((void *) iv, ivLength);
	      bytes.append(iv, ivLength);
	    }
	  else
	    {
	      ivLength = qMin(ivLength, static_cast<size_t> (bytes.length()));
	      memcpy((void *) iv,
		     (const void *) bytes.constData(),
		     ivLength);
	      bytes.remove(0, ivLength);
	    }

	  err = gcry_cipher_setiv(s_cipherCtx, (const void *) iv, ivLength);
	  gcry_free(iv);
	}
      else
	err = GPG_ERR_ENOMEM;
    }

  return err;
}

int dmisc::levenshteinDistance(const QString &str1,
			       const QString &str2)
{
  if(str1.isEmpty())
    return str2.length();
  else if(str2.isEmpty())
    return str1.length();

  int cost = 0;
  QChar str1_c = 0;
  QChar str2_c = 0;
  QVector<QVector<int> > matrix(str1.length() + 1,
				QVector<int> (str2.length() + 1));

  for(int i = 0; i <= str1.length(); i++)
    matrix[i][0] = i;

  for(int i = 0; i <= str2.length(); i++)
    matrix[0][i] = i;

  for(int i = 1; i <= str1.length(); i++)
    {
      str1_c = str1.at(i - 1);

      for(int j = 1; j <= str2.length(); j++)
	{
	  str2_c = str2.at(j - 1);

	  if(str1_c == str2_c)
	    cost = 0;
	  else
	    cost = 1;

	  matrix[i][j] = qMin(qMin(matrix[i - 1][j] + 1,
				   matrix[i][j - 1] + 1),
			      matrix[i - 1][j - 1] + cost);
	}
    }

  return matrix[str1.length()][str2.length()];
}

bool dmisc::passphraseWasAuthenticated(void)
{
  return s_passphraseWasAuthenticated;
}

QString dmisc::findUniqueFileName(const QString &fileName,
				  const QDir &path)
{
  QFileInfo fileInfo;

  fileInfo.setFile(path, fileName);

  if(fileInfo.exists())
    {
      int count = 1;
      QFileInfo info(fileName);

      if(info.suffix().trimmed().isEmpty())
	fileInfo.setFile(path, QString("%1(%2)").arg(fileName).arg(count));
      else
	fileInfo.setFile
	  (path,
	   QString("%1(%2).%3").arg(info.completeBaseName()).arg(count).
	   arg(info.suffix().trimmed()));

      while(fileInfo.exists())
	{
	  count += 1;

	  if(info.suffix().trimmed().isEmpty())
	    fileInfo.setFile(path, QString("%1(%2)").arg(fileName).arg(count));
	  else
	    fileInfo.setFile
	      (path, QString("%1(%2).%3").arg(info.completeBaseName()).
	       arg(count).
	       arg(info.suffix().trimmed()));
	}
    }

  return fileInfo.absoluteFilePath();
}

void dmisc::prepareFaviconsList(void)
{
  if(!passphraseWasAuthenticated())
    return;

  {
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE",
						"favicons");

    db.setDatabaseName(dooble::s_homePath +
		       QDir::separator() + "favicons.db");

    if(db.open())
      {
	s_faviconsList.clear();

	QSqlQuery query(db);

	if(query.exec("SELECT url, favicon FROM favicons WHERE "
		      "temporary = 0"))
	  while(query.next())
	    {
	      QUrl url
		(QUrl::fromEncoded
		 (decodedString
		  (QByteArray::fromBase64
		   (query.value(0).toByteArray())),
		  QUrl::StrictMode));

	      if(isSchemeAcceptedByDooble(url.scheme()))
		{
		  QIcon icon;
		  QBuffer buffer;
		  QByteArray bytes
		    (QByteArray::fromBase64(query.value(1).
					    toByteArray()));

		  bytes = decodedString(bytes);
		  buffer.setBuffer(&bytes);
		  buffer.open(QIODevice::ReadOnly);

		  QDataStream in(&buffer);

		  in >> icon;
		  buffer.close();

		  QPair<QUrl, QIcon> pair;

		  pair.first = url;
		  pair.second = icon;
		  s_faviconsList.append(pair);
		}
	    }
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("favicons");
}

void dmisc::reencodeFavicons(QProgressBar *progress)
{
  if(!passphraseWasAuthenticated())
    return;

  if(progress)
    {
      progress->setMaximum(-1);
      progress->setVisible(true);
    }

  {
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "favicons");

    db.setDatabaseName(dooble::s_homePath + QDir::separator() +
		       "favicons.db");

    if(db.open())
      {
	QSqlQuery query(db);

	query.exec("PRAGMA synchronous = OFF");
	query.exec("DELETE FROM favicons");
	query.exec("VACUUM");

	/*
	** Retrieve the expected number of changes.
	*/

	if(progress)
	  {
	    progress->setMaximum(s_faviconsList.size());
	    progress->update();
	  }

	int temporary = passphraseWasAuthenticated() ? 0 : 1;

	for(int i = 0; i < s_faviconsList.size(); i++)
	  {
	    QUrl url(s_faviconsList.at(i).first);
	    QIcon icon(s_faviconsList.at(i).second);
	    QBuffer buffer;
	    QByteArray bytes;

	    query.prepare
	      ("INSERT OR REPLACE INTO favicons "
	       "(url, url_hash, favicon, temporary) "
	       "VALUES (?, ?, ?, ?)");
	    query.bindValue
	      (0,
	       encodedString(url.toEncoded(QUrl::StripTrailingSlash).
			     toBase64(), true));
	    query.bindValue
	      (1,
	       hashedString(url.toEncoded(QUrl::StripTrailingSlash).
			    toBase64()));
	    buffer.setBuffer(&bytes);
	    buffer.open(QIODevice::WriteOnly);

	    QDataStream out(&buffer);

	    out << icon;
	    query.bindValue
	      (2,
	       encodedString(bytes, true).toBase64());
	    buffer.close();
	    query.bindValue(3, temporary);
	    query.exec();

	    if(progress)
	      progress->setValue(i + 1);
	  }
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("favicons");
  s_faviconsList.clear();

  if(progress)
    progress->setVisible(false);
}

void dmisc::clearFavicons(void)
{
  {
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "favicons");

    db.setDatabaseName(dooble::s_homePath + QDir::separator() +
		       "favicons.db");

    if(db.open())
      {
	QSqlQuery query(db);

	query.exec("PRAGMA synchronous = OFF");

	if(passphraseWasAuthenticated())
	  query.exec("DELETE FROM favicons");
	else
	  query.exec("DELETE FROM favicons WHERE temporary = 1");

	query.exec("VACUUM");
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("favicons");
}

void dmisc::purgeFavicons(void)
{
  {
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "favicons");

    db.setDatabaseName(dooble::s_homePath + QDir::separator() +
		       "favicons.db");

    if(db.open())
      {
	QSqlQuery query(db);

	query.exec("PRAGMA synchronous = OFF");
	query.exec("DELETE FROM favicons WHERE temporary = 1");
	query.exec("VACUUM");
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("favicons");
}

QRect dmisc::balancedGeometry(const QRect &geometry, QWidget *widget)
{
  /*
  ** Qt may place the window underneath a desktop's panel bar.
  ** The geometry variable contains the desired geometry.
  */

  QRect rect(geometry);
  QRect available(QApplication::desktop()->availableGeometry(widget));

  if(!available.contains(rect))
    {
      rect.setX(rect.x() + available.x());
      rect.setY(rect.y() + available.y());
#ifdef Q_WS_X11
      /*
      ** X11 is quite difficult to predict. Let's add this simple
      ** offset.
      */

      rect.setX(rect.x() + 50);
#endif

      if(rect.width() > available.width())
	rect.setWidth(0.85 * available.width());

      if(rect.height() > available.height())
	rect.setHeight(0.85 * available.height());
    }

  return rect;
}

bool dmisc::isSchemeAcceptedByDooble(const QString &scheme)
{
  QString l_scheme(scheme.toLower().trimmed());

  if(l_scheme == "file" ||
     l_scheme == "ftp" ||
     l_scheme == "http" ||
     l_scheme == "https" ||
     l_scheme == "qrc")
    return true;
  else
    return false;
}

void dmisc::removeRestorationFiles(const QUuid &id)
{
  if(passphraseWasAuthenticated())
    {
      /*
      ** Remove all restoration files or the files that are
      ** associated with the current process.
      */

      QDir dir(dooble::s_homePath + QDir::separator() + "Histories");
      QString idStr("");
      QFileInfoList files(dir.entryInfoList(QDir::Files, QDir::Name));

      if(!id.isNull())
	{
	  idStr = id.toString();
	  idStr.remove("{");
	  idStr.remove("}");
	  idStr.remove("-");
	}

      while(!files.isEmpty())
	{
	  QFileInfo fileInfo(files.takeFirst());

	  if(!idStr.isEmpty())
	    {
	      if(fileInfo.fileName().startsWith(idStr))
		QFile::remove(fileInfo.absoluteFilePath());
	    }
	  else
	    QFile::remove(fileInfo.absoluteFilePath());
	}
    }
}

void dmisc::removeRestorationFiles(const QUuid &pid,
				   const qint64 wid)
{
  if(passphraseWasAuthenticated())
    {
      /*
      ** Remove all restoration files that are associated
      ** with the current process and window.
      */

      QDir dir(dooble::s_homePath + QDir::separator() + "Histories");
      QString idStr(pid.toString().remove("{").remove("}").remove("-") +
		    QString::number(wid).rightJustified(20, '0'));
      QFileInfoList files(dir.entryInfoList(QDir::Files, QDir::Name));

      while(!files.isEmpty())
	{
	  QFileInfo fileInfo(files.takeFirst());

	  if(fileInfo.fileName().startsWith(idStr))
	    QFile::remove(fileInfo.absoluteFilePath());
	}
    }
}

void dmisc::setActionForFileSuffix(const QString &suffix,
				   const QString &action)
{
  if(!suffix.isEmpty())
    {
      if(action.isEmpty())
	dooble::s_applicationsActions[suffix] = "prompt";
      else
	dooble::s_applicationsActions[suffix] = action;
    }
  else
    /*
    ** Damaged suffix.
    */

    return;

  {
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE",
						"applications");

    db.setDatabaseName(dooble::s_homePath +
		       QDir::separator() + "applications.db");

    if(db.open())
      {
	QSqlQuery query(db);
	QByteArray bytes;

	query.exec("CREATE TABLE applications ("
		   "file_suffix TEXT PRIMARY KEY, "
		   "action TEXT)");

	if(query.exec(QString("SELECT icon FROM applications WHERE "
			      "file_suffix = '%1'").
		      arg(suffix)))
	  if(query.next())
	    bytes = query.value(0).toByteArray();

	if(bytes.isEmpty())
	  {
	    QBuffer buffer;
	    QFileIconProvider iconProvider;

	    buffer.setBuffer(&bytes);
	    buffer.open(QIODevice::WriteOnly);

	    QDataStream out(&buffer);

	    out << iconProvider.icon(QFileIconProvider::File).pixmap
	      (16, QIcon::Normal, QIcon::On);
	    bytes = bytes.toBase64();
	    buffer.close();
	  }

	query.exec("PRAGMA synchronous = OFF");
	query.prepare
	  ("INSERT OR REPLACE INTO applications ("
	   "file_suffix, action, icon) "
	   "VALUES (?, ?, ?)");
	query.bindValue(0, suffix);
	query.bindValue(1, action);
	query.bindValue(2, bytes);
	query.exec();
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("applications");
}

bool dmisc::canDoobleOpenLocalFile(const QUrl &url)
{
  static QStringList list;

  list << "gif"
       << "html"
       << "jpg"
       << "jpeg"
       << "png";

  QFileInfo fileInfo(url.toLocalFile());

  return list.contains(fileInfo.suffix().toLower().trimmed());
}

QString dmisc::fileNameFromUrl(const QUrl &url)
{
  QString path(url.path());
  QString fileName("");

  /*
  ** Attempt to gather the file name from the URL.
  */

  if(!path.isEmpty())
    fileName = QFileInfo(path).fileName();

  if(fileName.isEmpty())
    fileName = "dooble.download";

  return fileName;
}

void dmisc::launchApplication(const QString &program,
			      const QStringList &arguments)
{
#ifdef Q_OS_MAC
  QProcess::startDetached
    ("open", QStringList("-a") << program << "--args" << arguments);
#else
  QProcess::startDetached(program, arguments);
#endif
}

QIcon dmisc::iconForFileSuffix(const QString &suffix)
{
  QPixmap pixmap;

  {
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE",
						"applications");

    db.setDatabaseName(dooble::s_homePath +
		       QDir::separator() + "applications.db");

    if(db.open())
      {
	QSqlQuery query(db);

	if(query.exec(QString("SELECT icon FROM applications WHERE "
			      "file_suffix = '%1'").arg(suffix)))
	  if(query.next())
	    if(!query.isNull(0))
	      {
		QBuffer buffer;
		QByteArray bytes
		  (QByteArray::fromBase64(query.value(0).
					  toByteArray()));

		buffer.setBuffer(&bytes);
		buffer.open(QIODevice::ReadOnly);

		QDataStream in(&buffer);

		in >> pixmap;
		buffer.close();
	      }
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("applications");

  if(pixmap.isNull())
    {
      QFileIconProvider iconProvider;

      return iconProvider.icon(QFileIconProvider::File);
    }
  else
    return QIcon(pixmap);
}

bool dmisc::passphraseWasPrepared(void)
{
  QString str1(""), str2(""), str3("");

  if(dooble::s_settings.contains("settingsWindow/passphraseHash"))
    str1 = dooble::s_settings.value("settingsWindow/passphraseHash", "").
      toString();

  if(dooble::s_settings.contains("settingsWindow/passphraseHashType"))
    str2 = dooble::s_settings.value("settingsWindow/passphraseHashType", "").
      toString();

  if(dooble::s_settings.contains("settingsWindow/passphraseSalt"))
    str3 = dooble::s_settings.value("settingsWindow/passphraseSalt", "").
      toString();

  return !str1.isEmpty() && !str2.isEmpty() && !str3.isEmpty();
}

bool dmisc::isKDE(void)
{
  /*
  ** Trust the shell? Trust the user?
  */

  return QVariant(qgetenv("KDE_FULL_SESSION")).toBool();
}

bool dmisc::isGnome(void)
{
  /*
  ** Trust the shell? Trust the user?
  */

  return QVariant(qgetenv("DESKTOP_SESSION")).
    toString().toLower().trimmed() == "gnome";
}

void dmisc::logError(const QString &error)
{
  if(dooble::s_errorLog)
    dooble::s_errorLog->logError(error);
}

QString dmisc::elidedTitleText(const QString &text)
{
  QString l_text(text);

  if(l_text.length() > dooble::MAX_NUMBER_OF_MENU_TITLE_CHARACTERS)
    l_text = l_text.mid
      (0, dooble::MAX_NUMBER_OF_MENU_TITLE_CHARACTERS - 3).trimmed() + "...";

  l_text.replace("&", "&&");
  return l_text;
}

QString dmisc::formattedSize(const qint64 fileSize)
{
  QString str("");

  if(fileSize >= 0)
    {
      if(fileSize == 0)
	str = QObject::tr("0 Bytes");
      else if(fileSize == 1)
	str = QObject::tr("1 Byte");
      else if(fileSize < 1024)
	str = QString(QObject::tr("%1 Bytes")).arg(fileSize);
      else if(fileSize < 1048576)
	str = QString(QObject::tr("%1 KB")).arg
	  (QString::number(qRound(fileSize / 1024.0)));
      else
	str = QString(QObject::tr("%1 MB")).arg
	  (QString::number((fileSize * 1.0) / 1048576, 'f', 1));
    }
  else
    str = "0 Bytes";

  return str;
}

QUrl dmisc::correctedUrlPath(const QUrl &url)
{
  QUrl l_url(url);

  if(!l_url.path().isEmpty())
    {
      QString path(QDir::cleanPath(l_url.path()));

      path = path.remove("../");
      path = path.remove("/..");
      path = path.remove("/../");
      l_url.setPath(path);
    }

  return l_url;
}

QByteArray dmisc::hashedString(const QByteArray &byteArray)
{
  static int algorithm = -1;
  gcry_error_t err = 0;
  gcry_md_hd_t hd;
  QByteArray hashedArray(byteArray);;

  if(s_hashType == "sha512")
    algorithm = GCRY_MD_SHA512;
  else if(s_hashType == "sha384")
    algorithm = GCRY_MD_SHA384;
  else if(s_hashType == "sha256")
    algorithm = GCRY_MD_SHA256;
  else if(s_hashType == "sha224")
    algorithm = GCRY_MD_SHA224;
  else if(s_hashType == "tiger")
    algorithm = GCRY_MD_TIGER;
  else
    algorithm = GCRY_MD_SHA1;

  if((err = gcry_md_open(&hd, algorithm,
			 GCRY_MD_FLAG_SECURE |
			 GCRY_MD_FLAG_HMAC)) != 0)
    logError(QObject::tr("dmisc::hashedString(): "
			 "gcry_md_open() failure (%1).").
	     arg(gcry_strerror(err)));
  else
    {
      if((err = gcry_md_setkey(hd,
			       (const void *) s_passphraseHash,
			       s_passphraseHashLength)) != 0)
	logError(QObject::tr("dmisc::hashedString(): "
			     "gcry_md_setkey() failure (%1).").
		 arg(gcry_strerror(err)));
      else
	{
	  gcry_md_write(hd,
			(const void *) byteArray.constData(),
			(size_t) byteArray.length());

	  unsigned char *buffer = gcry_md_read(hd, algorithm);

	  if(buffer)
	    hashedArray = QByteArray((char *) buffer);
	  else
	    logError(QObject::tr("dmisc::hashedString(): "
				 "gcry_md_read() returned an empty "
				 "pointer."));
	}
    }

  gcry_md_close(hd);
  return hashedArray;
}
