/*
 * Copyright 2014 Cloud Linux Zug GmbH
 *
 * Licensed under CLOUD LINUX LICENSE AGREEMENT
 * http://cloudlinux.com/docs/LICENSE.TXT
 *
 * The part the support library for lsapi & proxy_lsapi Apache modules
 * author Alexander Demeshko <ademeshko@cloudlinux.com>
 *
 */

#ifndef _LSCAPI_H_
#define _LSCAPI_H_

#include <sys/uio.h>
#include <sys/types.h>
#include <stdint.h>

#include <lsapidef.h>
#include <lscapi_config.h>

// lsapi structs
typedef struct lsapi_req_header lsapi_req_header_t;
typedef struct lsapi_http_header_index lsapi_http_header_index_t;
typedef struct lsapi_header_offset lsapi_header_offset_t;
typedef struct lsapi_packet_header lsapi_packet_header_t;
typedef struct lsapi_resp_info lsapi_resp_info;

// lscapi opaque structures
struct lscapi_rec;
struct lsphp_conn_t;

typedef struct lscapi_rec lscapi_rec;
typedef struct lsphp_conn_t lsphp_conn_t;

// allocator function pointer type
typedef void *(*lscapi_alloc_fn)(size_t size, void *user_data);

typedef int (*lscapi_get_body_fn)(char *buf, size_t *bufLenPtr, void *user_data);

typedef int (*lscapi_should_get_block_fn)(void *user_data);

typedef void (*lscapi_log_fn)(const char *file, int line, int level,
                                const void *user_data, const char *fmt, ...);

typedef void (*lscapi_random_fn)(void *buf, size_t size);


typedef void* (*lscapi_lve_alloc_t)(size_t sz, void* user_data);
typedef void (*lscapi_lve_register_t)(void* user_data, void* data, int (*destroy_fn)());


struct lscapi_var_t {
    const char *key;
    int keyLen;
    const char *val;
    int valLen;
};
typedef struct lscapi_var_t lscapi_var_t;


struct lscapi_header_index_t {
    unsigned keyOff;
    unsigned keyLen;
    unsigned valOff;
    unsigned valLen;
};
typedef struct lscapi_header_index_t lscapi_header_index_t;


struct lscapi_resphdr_info_t {
    char buf[LSAPI_MAX_DATA_PACKET_LEN];
    size_t dataLen;
    uint16_t *hdrSizes;
    int hdrNum;
    int respStatus;
};
typedef struct lscapi_resphdr_info_t lscapi_resphdr_info_t;


struct lscapi_backend_info_t {
    uint32_t connect_tries;
    uint32_t connect_timeout;

    uint32_t backend_children;
    uint32_t backend_pgrp_max_idle;
    uint32_t backend_max_process_time;
};
typedef struct lscapi_backend_info_t lscapi_backend_info_t;


// Event masks
#define LSCAPI_BACKEND_LOG_RECEIVED (1u << 0)
#define LSCAPI_RESPONSE_FINISHED    (1u << 1)
#define LSCAPI_BACKEND_LOG_FATAL    (1u << 2)

enum lscapi_create_flags {
	LSCAPI_CREATE_WITH_LVE	= 1 << 0,
	LSCAPI_OUT_ALREADY_INSIDE_LVE	= 1 << 16,
	LSCAPI_OUT_RESOURCE_LIMIT_LVE	= 1 << 17,
};

int lscapi_init(char *errbuf, size_t errlen);
void lscapi_child_init(lscapi_alloc_fn user_alloc, void *user_data);

const char* lscapi_get_backend(const char *handler);

/**
 * create opaque structure to store connection to lsphp on logical level
 * args:
 * user_alloc = pointer to allocator function
 * user_data = arbitrary user data to pass to allocator function
 * uid = uid for suexec and lve
 * gid = gid for suexec and lve
 * flags = 
 * errbuf = buffer to store error description on error
 * errlen = length of the error buffer
 * return codes:
 * allocated and initialized structure = on success, NULL on error
 */
lscapi_rec *lscapi_create(lscapi_alloc_fn user_alloc,
                          lscapi_log_fn user_log,
                            void *user_data,
                            uint32_t uid, uint32_t gid,
                            unsigned *flagsPtr,
                            char *errbuf, unsigned errlen);


/**
 * close connection to lsphp on logical level
 * args:
 * lscapi = opaque structure created with lscapi_create
 */
void lscapi_destroy(lscapi_rec *lscapi);

int lscapi_send_request(lsphp_conn_t *conn, unsigned *eventMaskPtr,
                        char *errbuf, unsigned errlen);

void lscapi_set_userdir(lscapi_rec *lscapi, int userdir);
void lscapi_set_socket_path(lscapi_rec *lscapi, char *socket_path);

void lscapi_set_user_get_body(lscapi_rec *lscapi, size_t body_len, 
                              lscapi_get_body_fn user_get_body,
                              lscapi_should_get_block_fn user_should_get_block);

void lscapi_set_header_info(lscapi_rec *lscapi,
                            lsapi_http_header_index_t *hdrIndexPtr,
                            lsapi_header_offset_t *hdrOffsets,
                            size_t hdrOffsetsNum,
                            char *httpHeader,
                            size_t httpHeaderLen);

void lscapi_set_envs(lscapi_rec *lscapi, lscapi_var_t *envs, int envNum);
void lscapi_set_special_envs(lscapi_rec *lscapi, lscapi_var_t *specialEnvs, int specialEnvNum);

void lscapi_set_backend_path(lscapi_rec *lscapi, 
                             const char *sulsphp_path,
                             const char *handler, 
                             const char *backend_path);

void lscapi_set_debug_enabled(lscapi_rec *lscapi, int debug_enabled);

void lscapi_set_error(lscapi_rec *lscapi);

void lscapi_set_random(lscapi_rec *lscapi, lscapi_random_fn get_random_bytes);


const char* lscapi_get_backend_log(lscapi_rec *lscapi);


// lsphp stuff

void lscapi_init_backend_info(lscapi_backend_info_t *infoPtr);
void lscapi_merge_backend_info(lscapi_backend_info_t *base, lscapi_backend_info_t *cur, lscapi_backend_info_t *merged);

lsphp_conn_t* lscapi_acquire_lsphp_conn(lscapi_rec *lscapi, char *errbuf, unsigned errlen);

void lscapi_release_lsphp_conn(lsphp_conn_t *conn);

int lscapi_determine_conn_lsphp(lsphp_conn_t *conn, 
                                lscapi_backend_info_t *backendInfoPtr, 
                                char *errbuf, unsigned errlen);

int lscapi_connect_lsphp(lsphp_conn_t *conn, char *errbuf, unsigned errlen);

int lscapi_poll_lsphp(lsphp_conn_t *conn, int16_t reqevents);


int lscapi_receive_response_header(lsphp_conn_t *conn, 
                                   lscapi_resphdr_info_t *hdrInfoPtr,
                                   unsigned *eventMaskPtr,
                                   char *errbuf, unsigned errlen);
                                   
int lscapi_receive_response_chunk(lsphp_conn_t *conn,
                                  char *buf, size_t *lenPtr,
                                  unsigned *eventMaskPtr,
                                  char *errbuf, unsigned errlen);

// lve.c
/**
 * load LVE library by dlopen and initialize it
 * args:
 * lve_alloc = pointer to function to allocate memory
 * lve_register = pointer to function to register allocated block
 * lve_user_data = user data for lve_alloc and lve_register calls
 * errbuf = buffer to store error description on error
 * errlen = length of the error buffer
 * return codes:
 * 0 = on success, -1 on error
 */
int lscapi_load_lve(lscapi_lve_alloc_t lve_alloc, 
                    lscapi_lve_register_t lve_register, 
                    void *lve_user_data, 
                    char *errbuf, int errlen);

/**
 * check whether lscapi_load_lve was successfully called previously
 * return codes:
 * 1 = in case of success, 0 otherwise
 */
int lscapi_is_lve_loaded(void);


void lscapi_terminate_backends(void);

#endif // _LSCAPI_H_
