/**********
 * Copyright (c) 2003-2004 Greg Parker.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY GREG PARKER ``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 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **********/

#ifndef SESSION_H
#define SESSION_H

#include "includes.h"
#include "queue.h"
#include "ssh.h"
#include "openssh/buffer.h"
#include "openssh/compress.h"
#include "openssh/mac.h"
#include "openssh/cipher.h"
#include "crypto/openssl/dh/dh.h"
#include "crypto/openssl/evp/evp.h"
#include "vt100/vt100.h"

typedef enum {
    SESSION_STATE_STARTING = 0, 
    SESSION_STATE_OPEN, 
    SESSION_STATE_CLOSED
} session_state_t;

typedef enum {
    PACKETIZER_STATE_STARTING = 0, 
    PACKETIZER_STATE_WAITING_FOR_VERSION, 
    PACKETIZER_STATE_READING_NOT_VERSION, 
    PACKETIZER_STATE_READING_VERSION, 
    PACKETIZER_STATE_READING_PACKET_HEADER, 
    PACKETIZER_STATE_READING_PACKET_BODY, 
    PACKETIZER_STATE_CLOSED
} packetizer_state_t;

typedef enum {
    TRANSPORT_STATE_STARTING = 0, 
    TRANSPORT_STATE_WAITING_FOR_KEXINIT, 
    TRANSPORT_STATE_WAITING_FOR_DH_REPLY, 
    TRANSPORT_STATE_WAITING_FOR_NEWKEYS, 
    TRANSPORT_STATE_RUNNING, 
    TRANSPORT_STATE_CLOSED
} transport_state_t;

typedef enum {
    // ssh-userauth
    CONNECTION_STATE_STARTING = 0, 
    CONNECTION_STATE_WAITING_FOR_USERAUTH_SERVICE, 
    CONNECTION_STATE_WAITING_FOR_USERAUTH,

    // password auth
    CONNECTION_STATE_WAITING_FOR_PASSWORD, 
    CONNECTION_STATE_WAITING_FOR_NEW_PASSWORD, 

    // publickey auth
    CONNECTION_STATE_WAITING_FOR_PUBKEY_CHECK, 
    CONNECTION_STATE_WAITING_FOR_PUBKEY_PASSPHRASE, 

    // ssh-connection
    CONNECTION_STATE_WAITING_FOR_CHANNEL_OPEN, 
    CONNECTION_STATE_WAITING_FOR_PTY, 
    CONNECTION_STATE_WAITING_FOR_SHELL, 
    CONNECTION_STATE_RUNNING, 
    CONNECTION_STATE_CLOSING, 
    CONNECTION_STATE_CLOSED
} connection_state_t;


// mini state machine for publickey auth
typedef enum {
    PUBKEY_STARTING = 0, 
    PUBKEY_QUERY_PHRASELESS, 
    PUBKEY_QUERY_PHRASEFUL, 
    PUBKEY_GET_PASSPHRASE, 
    PUBKEY_DONE
} pubkey_state_t;

// pubkey auth and UI:
// Prefs holds default pubkey (if any) and per-connection pubkey (if any)
// If prefs has no pubkey, skip publickey auth.
// If prefs has a pubkey, send a validity check to the server. 
// If the validity check fails, present a "choose pubkey" dialog 
//   (if still invalid, repeat until user or server gives up)
// If the the validity check passes, present an "enter passphrase" dialog
//   (if wrong passphrase, repeat until user gives up)
//   (fixme can wrong passphrase be detected?)
// If the passphrase is correct, send the signature to the server
// If the signature is correct, we are logged in.
// If the signature is rejected, don't try any more pubkey auth.

typedef struct {
    char    *name;
    Cipher  *cipher;
    int     enabled;
    uint16_t   key_len;
    uint16_t   block_size;
    uint8_t  *key;
    uint8_t  *iv;
} Enc;

typedef struct {
    Enc enc;
    Mac mac;
    Comp comp;
} Keys;

typedef struct {
    packetizer_state_t state;

    // current crypto
    Keys inKeys;
    Keys outKeys;
    CipherContext inContext;
    CipherContext outContext;
    uint16_t inHeaderLength; // max(8, enc->block_size)
    uint16_t outHeaderLength; // max(8, enc->block_size)

    // version strings
    char *local_version_string;
    char *remote_version_string;

    // packet counters
    uint32_t seqnoIn;
    uint32_t seqnoOut;

    // receive buffers
    uint8_t paddingLength;
    uint32_t packetLength;
    Buffer unhandledData;
    Buffer decryptedData;

    // data handler
    // Err (*receiveData)(ssh_session_t *ss);
} packetizer_t;

typedef struct {
    transport_state_t state;
    uint8_t ignore_next_packet;

    // DH & kex
    uint16_t kex_key_length; 
    DH *dh;
    Buffer local_kexinit;  // kexinit payload sent by client
    Buffer remote_kexinit; // kexinit payload received from server
    int hostkey_type;      // key type for negotiated server host key algorithm
    uint8_t *session_id;   // current session ID hash (never gets rekeyed)
    uint16_t session_id_len;
    int rekey;             // FALSE if the first kex is not yet complete

    // pending crypto (to be used after NEWKEYS is sent)
    Keys newInKeys;
    Keys newOutKeys;

    // pending packets (to be sent after kex or rekey is complete)
    Buffer *packet_queue;
    int packet_queue_used;
    int packet_queue_allocated;

    // supported algorithms (comma-separated)
    char *supported_kex_algorithms;
    char *supported_hostkey_algorithms;
    char *supported_cipher_algorithms;
    char *supported_mac_algorithms;
    char *supported_compression_algorithms;
} transport_t;

typedef struct {
    connection_state_t state;

    uint32_t localChannel;  // sender for out, recipient for in
    uint32_t remoteChannel; // recipient for out, sender for in
    uint32_t receiveWindow; 
    uint32_t sendWindow;
    uint32_t sendMaxPacket;
    
    // login
    uint8_t passwordAttempts;
    char *untriedAuthmethods; // stuff we like and can still try
    char *usableAuthmethods;  // stuff the server likes

    // public keys
    pubkey_state_t pubkey_state;
    queue_t *phraseless_pubkeys;
    queue_t *phraseful_pubkeys;
    queue_t *good_pubkeys;
    MemHandle current_pubkey;

    // channel shutdown state
    uint8_t channelOpen;
    uint8_t localCloseSent;
    uint8_t remoteCloseReceived;
} connection_t;

typedef struct {
    session_state_t state;

    int socket;
    char *hostname;
    char *hostaddr;
} session_t;

typedef struct ssh_session_t {
    session_t s;    // socket
    packetizer_t p; // version exchange; read and decrypt packets
    transport_t t;  // key exchange; connection management
    connection_t c; // shell and user login

    display *tty;
    char *username;

    int closing; // do not refresh network while closing
} ssh_session_t;


int session_is_open(ssh_session_t *ss) SSH_SEGMENT;
int session_is_closing(ssh_session_t *ss) SSH_SEGMENT;
void session_close(ssh_session_t *ss) SSH_SEGMENT;
void session_kill(ssh_session_t *ss) SSH_SEGMENT;

#endif
