// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2012-2014 Monty Program Ab
// Copyright (c) 2015-2024 MariaDB Corporation Ab
package org.mariadb.jdbc.plugin.authentication.addon.gssapi;

import com.sun.jna.platform.win32.Sspi;
import com.sun.jna.platform.win32.SspiUtil;
import java.io.IOException;
import org.mariadb.jdbc.client.ReadableByteBuf;
import org.mariadb.jdbc.client.socket.Reader;
import org.mariadb.jdbc.client.socket.Writer;
import waffle.windows.auth.IWindowsSecurityContext;
import waffle.windows.auth.impl.WindowsSecurityContextImpl;

/** Waffle windows GSSAPI implementation */
public class WindowsNativeSspiAuthentication implements GssapiAuth {

  /**
   * Process native windows GSS plugin authentication.
   *
   * @param out out stream
   * @param in in stream
   * @param servicePrincipalName principal name
   * @param mechanisms gssapi mechanism
   * @throws IOException if socket error
   */
  public void authenticate(
      final Writer out, final Reader in, final String servicePrincipalName, final String mechanisms)
      throws IOException {

    // initialize a security context on the client
    IWindowsSecurityContext clientContext =
        WindowsSecurityContextImpl.getCurrent(mechanisms, servicePrincipalName);

    do {

      // Step 1: send token to server
      byte[] tokenForTheServerOnTheClient = clientContext.getToken();
      if (tokenForTheServerOnTheClient != null && tokenForTheServerOnTheClient.length > 0) {
        out.writeBytes(tokenForTheServerOnTheClient);
        out.flush();
      }
      if (!clientContext.isContinue()) {
        break;
      }

      // Step 2: read server response token
      ReadableByteBuf buf = in.readReusablePacket();
      byte[] tokenForTheClientOnTheServer = new byte[buf.readableBytes()];
      buf.readBytes(tokenForTheClientOnTheServer);
      Sspi.SecBufferDesc continueToken =
          new SspiUtil.ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, tokenForTheClientOnTheServer);
      clientContext.initialize(clientContext.getHandle(), continueToken, servicePrincipalName);

    } while (true);

    clientContext.dispose();
  }
}
