/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
 * Copyright (C) 2010 TigerVNC Team
 * Copyright (C) 2014-2021 m-privacy GmbH, Berlin
 * 
 * This 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 software 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 software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 * USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <assert.h>
#include <rfb/CSecurityNone.h>
#include <rfb/CSecurityStack.h>
#include <rfb/CSecurityVeNCrypt.h>
#include <rfb/CSecurityVncAuth.h>
#include <rfb/CSecurityPlain.h>
#include <rfb/CSecurityKrb.h>
#include <rfb/CSecurityMulti.h>
#include <rdr/Exception.h>
#include <rfb/Security.h>
#ifdef HAVE_GNUTLS
#include <rfb/CSecurityTLS.h>
#endif

#include <vncviewer/parameters.h>

using namespace rdr;
using namespace rfb;

UserPasswdGetter *CSecurity::upg = NULL;
#ifdef HAVE_GNUTLS
UserMsgBox *CSecurityTLS::msg = NULL;
#endif

StringParameter SecurityClient::secTypes
("SecurityTypes",
 "Specify which security scheme to use (TLSPlain, TLSPlainMulti, X509Cert, X509CertMulti, X509Krb, X509KrbMulti, X509Plain, X509PlainMulti, TLSKrb, None, VncAuth, VeNCrypt, Plain, PlainMulti, Krb, TLSNone, TLSVnc)",
 "TLSPlain,X509Cert,X509Krb,X509Plain,TLSKrb,None,VncAuth,VeNCrypt,Plain,Krb,TLSNone,TLSVnc",
 ConfViewer);

SecurityClient::SecurityClient() :  Security(secTypes, !noMulti), isMultiplexed(false), myMulti(NULL) {
}

void SecurityClient::stopMultiThreads() {
  if (!isMultiplexed || !myMulti)
    return;
  myMulti->runThreads = false;
}

CSecurity* SecurityClient::GetCSecurity(CConnection* cc, U32 secType)
{
  assert (CSecurity::upg != NULL); /* (upg == NULL) means bug in the viewer */
#ifdef HAVE_GNUTLS
  assert (CSecurityTLS::msg != NULL);
#endif

  if (!IsSupported(secType))
    goto bail;

  switch (secType) {
  case secTypeNone: return new CSecurityNone(cc);
  case secTypeVncAuth: return new CSecurityVncAuth(cc);
  case secTypeVeNCrypt: return new CSecurityVeNCrypt(cc, this);
  case secTypePlain: return new CSecurityPlain(cc);
  case secTypeKrb: return new CSecurityKrb(cc);
  case secTypePlainMulti:
    isMultiplexed = true;
    myMulti = new CSecurityMulti(cc);
    return new CSecurityStack(cc, secTypePlainMulti, "Username/Password and multiplexer",
                              myMulti, new CSecurityPlain(cc));
#ifdef HAVE_GNUTLS
  case secTypeTLSNone:
    return new CSecurityStack(cc, secTypeTLSNone,
                              "TLS with no password",
                              new CSecurityTLS(cc, true, false));
  case secTypeTLSVnc:
    return new CSecurityStack(cc, secTypeTLSVnc,
                              "TLS with VNCAuth",
                              new CSecurityTLS(cc, true, false),
                              new CSecurityVncAuth(cc));
  case secTypeTLSPlain:
    return new CSecurityStack(cc, secTypeTLSPlain,
                              "TLS with Username/Password",
                              new CSecurityTLS(cc, true, false),
                              new CSecurityPlain(cc));
  case secTypeTLSPlainMulti:
    isMultiplexed = true;
    myMulti = new CSecurityMulti(cc);
    return new CSecurityStack(cc, secTypeTLSPlainMulti, "TLS with Username/Password and multiplexer",
                              new CSecurityTLS(cc, true, false), myMulti, new CSecurityPlain(cc));
  case secTypeTLSKrb:
    return new CSecurityStack(cc, secTypeTLSKrb, "TLS with Kerberos Ticket",
                              new CSecurityTLS(cc, true, false), new CSecurityKrb(cc));
  case secTypeTLSKrbMulti:
    isMultiplexed = true;
    myMulti = new CSecurityMulti(cc);
    return new CSecurityStack(cc, secTypeTLSKrbMulti, "TLS with Kerberos Ticket and multiplexer",
                              new CSecurityTLS(cc, true, false), myMulti, new CSecurityKrb(cc));
  case secTypeX509None:
    return new CSecurityStack(cc, secTypeX509None,
                              "X509 with no password",
                              new CSecurityTLS(cc, false, false));
  case secTypeX509Vnc:
    return new CSecurityStack(cc, secTypeX509Vnc,
                              "X509 with VNCAuth",
                              new CSecurityTLS(cc, false, false),
                              new CSecurityVncAuth(cc));
  case secTypeX509Plain:
    return new CSecurityStack(cc, secTypeX509Plain,
			      "X509 with Username/Password",
			      new CSecurityTLS(cc, false, false), new CSecurityPlain(cc));
  case secTypeX509PlainMulti:
    isMultiplexed = true;
    myMulti = new CSecurityMulti(cc);
    return new CSecurityStack(cc, secTypeX509PlainMulti, "X509 with Username/Password and multiplexer",
                              new CSecurityTLS(cc, false, false), myMulti, new CSecurityPlain(cc));
  case secTypeX509Cert:
    return new CSecurityStack(cc, secTypeX509Cert, "X509 with User Cert",
                              new CSecurityTLS(cc, false, true));
  case secTypeX509CertMulti:
    isMultiplexed = true;
    myMulti = new CSecurityMulti(cc);
    return new CSecurityStack(cc, secTypeX509CertMulti, "X509 with User Cert and multiplexer",
                              new CSecurityTLS(cc, false, true), myMulti);
  case secTypeX509Krb:
    return new CSecurityStack(cc, secTypeX509Krb, "X509 with Kerberos Ticket",
                              new CSecurityTLS(cc, false, false), new CSecurityKrb(cc));
  case secTypeX509KrbMulti:
    isMultiplexed = true;
    myMulti = new CSecurityMulti(cc);
    return new CSecurityStack(cc, secTypeX509KrbMulti, "X509 with Kerberos Ticket and multiplexer",
                              new CSecurityTLS(cc, false, false), myMulti, new CSecurityKrb(cc));
#endif
  }

bail:
  throw Exception("Security type not supported");
}

void SecurityClient::setDefaults()
{
#ifdef HAVE_GNUTLS
    CSecurityTLS::setDefaults();
#endif
}
