#ifndef _sys_windows_SslAsynchIO
#define _sys_windows_SslAsynchIO

/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

#include "qpid/sys/AsynchIO.h"
#include "qpid/sys/IntegerTypes.h"
#include "qpid/sys/Poller.h"
#include "qpid/CommonImportExport.h"
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <windows.h>
// security.h needs to see this to distinguish from kernel use.
#define SECURITY_WIN32
#include <security.h>
#include <Schnlsp.h>
#undef SECURITY_WIN32

namespace qpid {
namespace sys {
namespace windows {
    
/*
 * SSL/Schannel shim between the frame-handling and AsynchIO layers.
 * SslAsynchIO creates a regular AsynchIO object to handle I/O and this class
 * gets involved for SSL negotiations and encrypt/decrypt. The details of
 * how this all works are invisible to the layers on either side. The only
 * change from normal AsynchIO usage is that there's an extra callback
 * from SslAsynchIO to indicate that the initial session negotiation is
 * complete.
 *
 * The details of session negotiation are different for client and server
 * SSL roles. These differences are handled by deriving separate client
 * and server role classes.
 */
class SslAsynchIO : public qpid::sys::AsynchIO {
public:
    typedef boost::function1<void, SECURITY_STATUS> NegotiateDoneCallback;

    SslAsynchIO(const qpid::sys::Socket& s,
                CredHandle hCred,
                ReadCallback rCb,
                EofCallback eofCb,
                DisconnectCallback disCb,
                ClosedCallback cCb = 0,
                BuffersEmptyCallback eCb = 0,
                IdleCallback iCb = 0,
                NegotiateDoneCallback nCb = 0);
    ~SslAsynchIO();

    virtual void queueForDeletion();

    virtual void start(qpid::sys::Poller::shared_ptr poller);
    virtual void queueReadBuffer(BufferBase* buff);
    virtual void unread(BufferBase* buff);
    virtual void queueWrite(BufferBase* buff);
    virtual void notifyPendingWrite();
    virtual void queueWriteClose();
    virtual bool writeQueueEmpty();
    virtual void startReading();
    virtual void stopReading();
    virtual void requestCallback(RequestCallback);
    virtual BufferBase* getQueuedBuffer();

    QPID_COMMON_EXTERN unsigned int getSslKeySize();

protected:
    CredHandle credHandle;

    // AsynchIO layer below that's actually doing the I/O
    qpid::sys::AsynchIO *aio;

    // Track what the state of the SSL session is. Have to know when it's
    // time to notify the upper layer that the session is up, and also to
    // know when it's not legit to pass data through to either side.
    enum { Negotiating, Running, Redo, ShuttingDown } state;
    bool sessionUp;
    CtxtHandle ctxtHandle;
    TimeStamp credExpiry;

    // Client- and server-side SSL subclasses implement these to do the
    // proper negotiation steps. negotiateStep() is called with a buffer
    // just received from the peer.
    virtual void startNegotiate() = 0;
    virtual void negotiateStep(BufferBase *buff) = 0;

    // The negotiating steps call one of these when it's finalized:
    void negotiationDone();
    void negotiationFailed(SECURITY_STATUS status);

private:
    // These are callbacks from AsynchIO to here.
    void sslDataIn(qpid::sys::AsynchIO& a, BufferBase *buff);
    void idle(qpid::sys::AsynchIO&);

    // These callbacks are to the layer above.
    ReadCallback readCallback;
    IdleCallback idleCallback;
    NegotiateDoneCallback negotiateDoneCallback;
    volatile unsigned int callbacksInProgress;   // >0 if w/in callbacks
    volatile bool queuedDelete;

    // Address of peer, in case it's needed for logging.
    std::string peerAddress;

    // Partial buffer of decrypted plaintext given back by the layer above.
    AsynchIO::BufferBase *leftoverPlaintext;

    SecPkgContext_StreamSizes schSizes;
};

/*
 * SSL/Schannel client-side shim between the frame-handling and AsynchIO
 * layers.
 */
class ClientSslAsynchIO : public SslAsynchIO {
public:
    // Args same as for SslIoShim, with the addition of brokerHost which is
    // the expected SSL name of the server.
    QPID_COMMON_EXTERN ClientSslAsynchIO(const std::string& brokerHost,
                                         const qpid::sys::Socket& s,
                                         CredHandle hCred,
                                         ReadCallback rCb,
                                         EofCallback eofCb,
                                         DisconnectCallback disCb,
                                         ClosedCallback cCb = 0,
                                         BuffersEmptyCallback eCb = 0,
                                         IdleCallback iCb = 0,
                                         NegotiateDoneCallback nCb = 0);

private:
    std::string serverHost;

    // Client- and server-side SSL subclasses implement these to do the
    // proper negotiation steps. negotiateStep() is called with a buffer
    // just received from the peer.
    void startNegotiate();
    void negotiateStep(BufferBase *buff);
};
/*
 * SSL/Schannel server-side shim between the frame-handling and AsynchIO
 * layers.
 */
class ServerSslAsynchIO : public SslAsynchIO {
public:
    QPID_COMMON_EXTERN ServerSslAsynchIO(bool clientMustAuthenticate,
                                         const qpid::sys::Socket& s,
                                         CredHandle hCred,
                                         ReadCallback rCb,
                                         EofCallback eofCb,
                                         DisconnectCallback disCb,
                                         ClosedCallback cCb = 0,
                                         BuffersEmptyCallback eCb = 0,
                                         IdleCallback iCb = 0,
                                         NegotiateDoneCallback nCb = 0);

private:
    bool clientAuth;

    // Client- and server-side SSL subclasses implement these to do the
    // proper negotiation steps. negotiateStep() is called with a buffer
    // just received from the peer.
    void startNegotiate();
    void negotiateStep(BufferBase *buff);
};

}}}   // namespace qpid::sys::windows

#endif // _sys_windows_SslAsynchIO
