/*! @file

    @id $Id: main.cxx 15 2010-06-03 14:17:27Z marc $

    Build für Windoof:
    <code>QMAKESPEC=$(pwd)/mkspecs/mingw-g++ qmake-qt4 && make release</code>
*/
//       1         2         3         4         5         6         7         8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890

#include <qbrowserlib/log.hxx>
#include <browser.hxx>

#include <QtGui/QApplication>
#include <QtCore/QTranslator>
#include <QtCore/QTextCodec>
#include <QtCore/QLocale>
#include <QtCore/QProcess>
#include <QtCore/QFileInfo>
#include <QtNetwork/QNetworkProxy>
#include <QtNetwork/QSslConfiguration>
#include <QtNetwork/QSslCertificate>
#include <QtNetwork/QSslKey>
#include <QtWebKit/QWebSettings>

#include <iostream>
#include <sstream>

#include <memory>


//! @defgroup browser The SwissBrowser Executable and Main Window
//! @defgroup pkcs11 The PKCS#11 Client Authentication
//! @defgroup qbrowserlib The Swiss Browser Library
//! @defgroup designer Qt Designer Plugin for Browser Related Widgets

/*! @mainpage The Secure Swiss PKCS#11 WebBrowser

    The browser is divided into a library, @ref qbrowserlib that implements
    generic browsing utilities, the main @ref browser page that collects
    those features into a main window, a @ref pkcs11 Post-SuisseID
    hardware token client authentication and a @ref designer plugin for
    flexible GUI creation.
    */

//! @addtogroup browser
//! @{

QString TMP;

extern QWEBKIT_EXPORT void qt_drt_overwritePluginDirectories();

QMap<QString, QString>& env() {
  static QStringList l(QProcess::systemEnvironment());
  static QMap<QString, QString> env;
  if (env.isEmpty())
    for (QStringList::iterator it(l.begin()); it!=l.end(); ++it) {
      QStringList v(it->split('='));
      QString key(*v.begin());
      QString value((v.pop_front(), v.join("=")));
      env.insert(key, value);
    }
  return env;
}

QString helptext() {
  return QObject::trUtf8
    ("Usage: %1 [OPTIONS...] [<url> ...]\n"
     "Options:\n"
     " -h, --help        show this help text\n"
     " -d, --debug       enable verbose debug mode\n"
     " -t  --tmp path    temporary folder for data caching\n"
     " -k, --kiosk       no url bar\n"
     "                   if you sepcify -k and -s, -k must be first\n"
     " -q, --quirks      alternate user interface\n"
     " -n, --no-settings don't load or store any settings\n"
     " --login           ask for smartcard password at startup\n"
     " -l, --lib <file>  path to file libengine_act.so\n"
     " -s, --settings <file>\n"
     "                   load settings from <file>\n"
     "                   if you sepcify -k and -s, -k must be first\n"
     " -c, --cert <file> load local client certificate from <file>\n"
     " -y, --key  <file> load local certificate key from <file>\n"
     " -m, --mime  <mime> <ext> <tool>\n"
     "                   start <tool> for mimetype <mime>\n"
     " -b, --bookmarks <file>\n"
     "                   load and save bookmarks from and to <file>\n"
     " -e, --edit-bookmarks\n"
     "                   start in bookmark-edition mode\n"
     " <url>             optional full URL\n"
     "Environment:\n"
     "  LANGUAGE              \"de\", \"en\", ...               "
     "(actual: %5)\n"
     "  PROXY_TYPE            \"http\" or \"socks\" or \"\"       "
     "(actual: %2)\n"
     "  PROXY_PORT            proxy port number             (actual: %3)\n"
     "  PROXY_HOST            proxy host name               (actual: %4)\n"
     "  SWISS_USERAGENT       fake user agent               (actual: %6)\n"
     "  QT_PLUGIN_PATH        path to plugins dir           (actual: %7)\n"
     "  QTWEBKIT_PLUGIN_PATH  path to netscape plugins      (actual: %8)")
    .arg(QFileInfo(QCoreApplication::arguments().at(0)).fileName())
    .arg(env()["PROXY_TYPE"]).arg(env()["PROXY_PORT"]).arg(env()["PROXY_HOST"])
    .arg(env()["LANGUAGE"]).arg(env()["SWISS_USERAGENT"])
    .arg(env()["QT_PLUGIN_PATH"]).arg(env()["QTWEBKIT_PLUGIN_PATH"]);
}

int main(int argv, char** argc) try {
  //............................................................................
  QSslConfiguration sslConfig(QSslConfiguration::defaultConfiguration());
  sslConfig.setProtocol(QSsl::AnyProtocol);
  sslConfig.setPeerVerifyMode(QSslSocket::AutoVerifyPeer);
  //............................................................................
  QTextCodec* utf8(QTextCodec::codecForName("UTF-8"));
  QTextCodec::setCodecForCStrings(utf8);
  QTextCodec::setCodecForLocale(utf8);
  QTextCodec::setCodecForTr(utf8);
  QApplication app(argv, argc);
  app.setWindowIcon(QIcon(":/icons/32x32/swissbrowser.png"));
  app.setApplicationName(QObject::trUtf8("SwissBrowser", "application name"));
  app.setApplicationVersion(VERSION);
  QTranslator qtTranslator;
  if (env().contains("LANGUAGE")) QLocale::setDefault(env()["LANGUAGE"]);
  qtTranslator.load(":/language/qt_" + QLocale().name());
  app.installTranslator(&qtTranslator);
  QTranslator appTranslator;
  appTranslator.load(":/language/swissbrowser_"+ QLocale().name());
  app.installTranslator(&appTranslator);
  //----------------------------------------------------------------------------
  TMP=QDir::toNativeSeparators(QDir::tempPath());
  QStringList urls;
  QString actlib
#ifdef Q_OS_LINUX
    ("libcvP11.so")
#else
#ifdef Q_OS_MAC
    ("libcvP11.dylib")
#else
#ifdef Q_OS_WIN
    ("cvP11.dll")
#endif
#endif
#endif
    ;
  QWebSettings::globalSettings()->setAttribute
    (QWebSettings::PluginsEnabled, true);
  bool silent(false);
  bool quirks(false);
  bool login(false);
  qbrowserlib::Settings::MimeTypes mimetypes;
  QString bookmarks;
  bool editbookmarks(false);
  QStringList args(app.arguments());
  std::auto_ptr<QSettings> settings
    (std::auto_ptr<QSettings>(new QSettings("SwissSign", "SwissBrowser")));
  for (QStringList::iterator it(args.begin()); ++it!=args.end();)
    if (*it=="-h" || *it=="--help" || *it=="-help" || *it=="/?") {
      std::cout<<helptext().toStdString()<<std::endl;
      return 0;
    } else if (*it=="-d" || *it=="--debug") {
      qbrowserlib::Log::DEBUG = true;
    } else if ((*it=="-t" || *it=="--tmp") && ++it!=args.end()) {
      TMP=*it;
    } else if ((*it=="-k" || *it=="--kiosk")) {
      silent=true;
      settings.reset();
    } else if ((*it=="-q" || *it=="--quirks")) {
      quirks=true;
    } else if ((*it=="-n" || *it=="--no-settings")) {
      settings.reset();
    } else if (*it=="--login") {
      login = true;
    } else if ((*it=="-l" || *it=="--lib") && ++it!=args.end()) {
      actlib = *it;
    } else if ((*it=="-b" || *it=="--bookmarks") && ++it!=args.end()) {
      bookmarks = *it;
    } else if ((*it=="-e" || *it=="--edit-bookmarks")) {
      editbookmarks = true;
    } else if ((*it=="-s" || *it=="--settings") && ++it!=args.end()) {
      settings = std::auto_ptr<QSettings>
        (new QSettings(*it, QSettings::IniFormat));
    } else if ((*it=="-c" || *it=="--cert") && ++it!=args.end()) {
      QFile file(*it);
      file.open(QIODevice::ReadOnly);
      QSslCertificate c(&file);
      if (c.isNull()) {
        std::cerr<<QObject::trUtf8("Cannot read PEM certificate from file: %1")
          .arg(*it).toStdString()<<std::endl;
        return 1;
      }
      sslConfig.setLocalCertificate(c);
      file.close();
      std::cout<<QObject::trUtf8("Read PEM certificates from file: %1")
        .arg(*it).toStdString()<<std::endl;
    } else if ((*it=="-y" || *it=="--key") && ++it!=args.end()) {
      QFile file(*it);
      file.open(QIODevice::ReadOnly);
      QSslKey k(&file, QSsl::Rsa);
      if (k.isNull()) {
        std::cerr<<QObject::trUtf8("Cannot read PEM RSA key from file: %1")
          .arg(*it).toStdString()<<std::endl;
        return 1;
      }
      sslConfig.setPrivateKey(k);
      std::cout<<QObject::trUtf8("Read private key from file: %1")
        .arg(*it).toStdString()<<std::endl;
    } else if ((*it=="-m" || *it=="--mime")
               && ++it!=args.end() && ++it!=args.end() && ++it!=args.end()) {
      QString mt(*----it);
      QString ext(*++it);
      QString app(*++it);
      mimetypes[mt] = QStringList()<<ext<<app;
    } else if (it!=args.end()) {
      urls<<*it;
    } else {
      std::cerr<<QObject::trUtf8("Too few arguments.\nTry: %1 --help")
        .arg(QFileInfo(argc[0]).fileName()).toStdString()<<std::endl;
      return 1;
    }
  TRC_FN;
  QList<QSslCertificate> certs(sslConfig.caCertificates());
  for (QList<QSslCertificate>::iterator cert(certs.begin());
       cert!=certs.end(); ++cert) {
    LOG<<"CERTIFICATE: "
       <<cert->issuerInfo(QSslCertificate::Organization)
       <<cert->issuerInfo(QSslCertificate::CommonName)
       <<cert->subjectInfo(QSslCertificate::Organization)
       <<cert->subjectInfo(QSslCertificate::CommonName);
  }
  QSslConfiguration::setDefaultConfiguration(sslConfig);
  //............................................................................
  if (env()["PROXY_TYPE"]=="http")
    QNetworkProxy::setApplicationProxy
      (QNetworkProxy
       (QNetworkProxy::HttpProxy,
        env()["PROXY_HOST"], env()["PROXY_PORT"].toInt()));
  if (env()["PROXY_TYPE"]=="socks")
    QNetworkProxy::setApplicationProxy
      (QNetworkProxy
       (QNetworkProxy::Socks5Proxy,
        env()["PROXY_HOST"], env()["PROXY_PORT"].toInt()));
  else
    QNetworkProxyFactory::setUseSystemConfiguration(true);
  LOG<<"Start - Proxy:"
     <<(QNetworkProxy::applicationProxy().type()==QNetworkProxy::NoProxy
        ? "No Proxy"
        : (QNetworkProxy::applicationProxy().type()
           ==QNetworkProxy::Socks5Proxy ? "socks" : "http"))
     <<"Hostname"<<QNetworkProxy::applicationProxy().hostName()
     <<"Port"<<QNetworkProxy::applicationProxy().port();
  //............................................................................
  if (env().contains("QTWEBKIT_PLUGIN_PATH"))
    qt_drt_overwritePluginDirectories();
  Browser browser(actlib, urls, settings.get(), mimetypes, silent,
                  login, quirks, bookmarks);
  if (editbookmarks) browser.on_actionEditBookmarks_triggered();
  return app.exec();
 } catch (std::exception& x) {
  TRC_FN; LOG<<"Terminated with Exception"<<x.what();
  std::cerr<<"**** Terminated with Error:"
           <<std::endl<<x.what()<<std::endl;
  return 1;
 } catch (...) {
  // unexpected exception - just terminate
  TRC_FN; LOG<<"Terminated with unknown Exception";
  std::cerr<<"**** Terminated with unknown Error"<<std::endl;
  return 1;
 }

//! @}
