/*
 * %CopyrightBegin%
 * 
 * Copyright Ericsson AB 2008-2021. All Rights Reserved.
 * 
 * Licensed 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.
 * 
 * %CopyrightEnd%
 */

/* A protocol decoder. Simple packet length extraction as well as packet
 * body parsing with protocol specific callback interfaces (http and ssl).
 */
#ifndef __PACKET_PARSER_H__
#define __PACKET_PARSER_H__

#include <erl_driver.h>
#include "sys.h"


/* INET_LOPT_PACKET options */
enum PacketParseType {
    TCP_PB_RAW      = 0,
    TCP_PB_1        = 1,
    TCP_PB_2        = 2,
    TCP_PB_4        = 3,
    TCP_PB_ASN1     = 4,
    TCP_PB_RM       = 5,
    TCP_PB_CDR      = 6,
    TCP_PB_FCGI     = 7,
    TCP_PB_LINE_LF  = 8,
    TCP_PB_TPKT     = 9,
    TCP_PB_HTTP     = 10,
    TCP_PB_HTTPH    = 11,
    TCP_PB_SSL_TLS  = 12,
    TCP_PB_HTTP_BIN = 13,
    TCP_PB_HTTPH_BIN = 14
};

typedef struct http_atom {
    struct http_atom* next;   /* next in bucket */
    unsigned long h;          /* stored hash value */
    const char* name;
    int   len;
    int index;                /* index in table + bit-pos */
    ErlDrvTermData atom;      /* erlang atom rep */
} http_atom_t;  

typedef struct {
    enum {
        URI_STAR,    /* '*' */
        URI_STRING,  /* "string(s1)" */
        URI_ABS_PATH,/* {abs_path, "path(s1)"} */
        URI_SCHEME,  /* {scheme, "scheme(s1)", "string(s2)"} */
        URI_HTTP,    /* {absoluteURI, http, "host(s1)", Port, "path(s2)"} */
        URI_HTTPS    /* {absoluteURI, https, ... */
    } type;
    const char* s1_ptr;
    int s1_len;
    const char* s2_ptr;
    int s2_len;
    int port; /* 0=undefined */
}PacketHttpURI;

typedef int HttpResponseMessageFn(void* arg, int major, int minor, int status,
				  const char* phrase, int phrase_len);
typedef int HttpRequestMessageFn(void* arg, const http_atom_t* meth, const char* meth_ptr,
				 int meth_len, const PacketHttpURI*, int major, int minor);
typedef int HttpEohMessageFn(void *arg);
typedef int HttpHeaderMessageFn(void* arg, const http_atom_t* name,
				const char* name_ptr, int name_len,
				const char* oname_ptr, int oname_len,
				const char* value_ptr, int value_len);
typedef int HttpErrorMessageFn(void* arg, const char* buf, int len);
typedef int SslTlsFn(void* arg, unsigned type, unsigned major, unsigned minor,
                     const char* data, int len, const char* prefix, int plen);

typedef struct {
    HttpResponseMessageFn* http_response;
    HttpRequestMessageFn* http_request;
    HttpEohMessageFn* http_eoh;
    HttpHeaderMessageFn* http_header;
    HttpErrorMessageFn* http_error;
    SslTlsFn* ssl_tls;
}PacketCallbacks;


/* Called once at emulator start
 */
void packet_parser_init(void);

/* Returns > 0 Total packet length.
 *         = 0 Length unknown, need more data.
 *         < 0 Error, invalid format.
 */
int packet_get_length(enum PacketParseType htype,
		      const char* ptr, unsigned n,  /* Bytes read so far */
		      unsigned max_plen,      /* Packet max length, 0=no limit */
		      unsigned trunc_len,     /* Truncate (lines) if longer, 0=no limit */
		      char     delimiter,     /* Line delimiting character */
		      int*     statep);       /* Internal protocol state */

ERTS_GLB_INLINE
void packet_get_body(enum PacketParseType htype,
                     const char** bufp, /* In: Packet header, Out: Packet body */
                     int* lenp);        /* In: Packet length, Out: Body length */

/* Returns 1 = Packet parsed and handled by callbacks.
**         0 = No parsing support for this packet type
**        -1 = Error
*/
ERTS_GLB_INLINE
int packet_parse(enum PacketParseType htype, 
		 const char* buf, int len, /* Total packet */
		 int* statep, PacketCallbacks* pcb, void* arg);



/* Internals for the inlines below: */

#define FCGI_VERSION_1 1
struct fcgi_head {
    unsigned char version;
    unsigned char type;
    unsigned char requestIdB1;
    unsigned char requestIdB0;
    unsigned char contentLengthB1;
    unsigned char contentLengthB0;
    unsigned char paddingLength;
    unsigned char reserved;
    /* char data[] */
    /* char padding[paddingLength] */
};
int packet_parse_http(const char*, int, int*, PacketCallbacks*, void*);
int packet_parse_ssl(const char*, int, PacketCallbacks*, void*);


#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE
void packet_get_body(enum PacketParseType htype, const char** bufp, int* lenp)
{
    switch (htype) {
    case TCP_PB_1:  *bufp += 1; *lenp -= 1; break;
    case TCP_PB_2:  *bufp += 2; *lenp -= 2; break;
    case TCP_PB_4:  *bufp += 4; *lenp -= 4; break;
    case TCP_PB_FCGI:
	*lenp -= ((struct fcgi_head*)*bufp)->paddingLength;
        break;
    default:
        ;/* Return other packets "as is" */
    }
}

ERTS_GLB_INLINE
int packet_parse(enum PacketParseType htype, const char* buf, int len,
		 int* statep, PacketCallbacks* pcb, void* arg)
{	
    switch (htype) {
    case TCP_PB_HTTP:
    case TCP_PB_HTTPH:
    case TCP_PB_HTTP_BIN:
    case TCP_PB_HTTPH_BIN:
        if (packet_parse_http(buf, len, statep, pcb, arg) < 0)
            pcb->http_error(arg, buf, len);
        return 1;
    case TCP_PB_SSL_TLS:
	return packet_parse_ssl(buf, len, pcb, arg);
    default:;
    }
    return 0;
}
#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */

#endif /* !__PACKET_PARSER_H__ */

