/*
 * Copyright (c) Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2018 All Rights Reserved
 *
 * 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>
 *
 * Here you define functions are to be used (means directly called) from
 * mod_lsapi (mod_lsapi/mod_lsapi.c + liblscapi-util/ *.c)
 */

#ifndef _LSCAPI_UTIL_H_
#define _LSCAPI_UTIL_H_

#include <lscapi.h>

#include <httpd.h>
#include <http_config.h>
#include <http_core.h>
#include <http_log.h>
#include <http_protocol.h>
#include <unixd.h>
#include <apr_strings.h>

#ifdef WITH_BLACKLIST
#include <json-c/json.h>
#endif

#ifdef LSCAPI_WITH_DUMP_HEADER
#undef LSCAPI_WITH_DUMP_HEADER
#endif

#if AP_MODULE_MAGIC_AT_LEAST(20100504,0)

#define LSCAPI_WITH_MUTEX_API 1

#else

#define LSCAPI_WITH_MUTEX_API 0

#endif

#ifdef APACHE2_2

#define APLOGNO(n)              "AH" #n ": "

#define APLOG_USE_MODULE(foo) \
    extern module AP_MODULE_DECLARE_DATA foo##_module

#else

extern const int *lsapilog_module_index;

#ifdef APLOG_MODULE_INDEX
#undef APLOG_MODULE_INDEX
#endif

#define APLOG_MODULE_INDEX  \
    (aplog_module_index ? *aplog_module_index : (lsapilog_module_index ? *lsapilog_module_index : APLOG_NO_MODULE) )
#endif

/* Default value is good for almost every arch. Except httpd24, it should help itself. */
#ifndef LSAPI_SOCK_PATH
#define LSAPI_SOCK_PATH "/var/mod_lsapi"
#endif //LSAPI_SOCK_PATH

#ifndef LSAPI_LOG_PATH
#define LSAPI_LOG_PATH "/var/log/mod_lsapi"
#endif

#define SULSPHP_LOG_NAME "sulsphp_log"
#define SULSPHP_LOG_UNINITIALIZED (void*)0

/* LSAPI_PRO_ONLY:BEGIN */
#ifdef WITH_LIBPERFLOG
extern int lsapi_use_perflog;
#endif // WITH_LIBPERFLOG

/* LSAPI_PRO_ONLY:END */
enum {
   LSAPI_PHPRC_SOURCE_NO = 0,
   LSAPI_PHPRC_SOURCE_VAL,
   LSAPI_PHPRC_SOURCE_AUTO,
   LSAPI_PHPRC_SOURCE_ENV,
   LSAPI_PHPRC_SOURCE_DOCROOT
};


struct lsapi_svr_conf_t {
    char *socket_path;
    char *phprc;
    char *selfstarter_socket_path;
    char *hostname_on_debug;
    char *protocol_log_file;
    const char *backend_env_path;
    const char *tmpdir;
    uint32_t default_uid;
    uint32_t default_gid;
    uint32_t max_pool_size;
#if !LSCAPI_WITH_MUTEX_API
    apr_lockmech_e mutex_mech;
#endif

#ifdef WITH_BLACKLIST
    json_object *handler_blacklist;
    time_t blacklist_last_mod_time;
#endif

    lscapi_backend_info_t backend_info;
    apr_table_t *envTable;

    char *sulsphp_log_path;

/* LSAPI_PRO_ONLY:BEGIN */
    char *criu_socket_path;
    char *criu_imgs_dir_path;

/* LSAPI_PRO_ONLY:END */
    unsigned lve_enabled:1;
    unsigned debug_enabled:1;
    unsigned terminate_backends_on_exit: 1;
    unsigned check_target_perm: 1;
    unsigned paranoid: 1;
    unsigned use_default_uid:1;
    unsigned skip_check_document_root:1;
    unsigned accept_notify: 1;
    unsigned phprc_source:3;
/* LSAPI_PRO_ONLY:BEGIN */
    unsigned mutex_tweak: 1;
    unsigned with_connection_pool: 1;
/* LSAPI_PRO_ONLY:END */
    unsigned use_req_hostname: 1;
    unsigned server_tweak: 1;

    unsigned debug_enabled_was_set:1;
    unsigned terminate_backends_on_exit_was_set: 1;
    unsigned check_target_perm_was_set: 1;
    unsigned paranoid_was_set: 1;
    unsigned use_default_uid_was_set:1;
    unsigned skip_check_document_root_was_set:1;
    unsigned phprc_source_was_set:1;
    unsigned accept_notify_was_set: 1;
/* LSAPI_PRO_ONLY:BEGIN */
    unsigned mutex_tweak_was_set: 1;
    unsigned with_connection_pool_was_set: 1;
/* LSAPI_PRO_ONLY:END */
    unsigned use_req_hostname_was_set: 1;
    unsigned server_tweak_was_set: 1;
};
typedef struct lsapi_svr_conf_t lsapi_svr_conf_t;

struct php_param_t {
    char *value;
    size_t valueLen;
    int isAdmin;
    int htaccess;
};
typedef struct php_param_t php_param_t;

struct lsapi_dir_conf_t {
    apr_hash_t *phpParams;
    uint32_t lsapi_uid;
    uint32_t lsapi_gid;
    uint32_t suphp_uid;
    uint32_t suphp_gid;
    uint32_t ruid;
    uint32_t rgid;
    uint32_t itk_uid;
    uint32_t itk_gid;

    int err_server_docroot;       // default HTTP_INTERNAL_SERVER_ERROR
    int err_server_uid;           // default HTTP_INTERNAL_SERVER_ERROR
    int err_script_perms;         // default HTTP_INTERNAL_SERVER_ERROR

    int err_lsapi_create;         // default HTTP_SERVICE_UNAVAILABLE
    int err_lsapi_internal;       // default HTTP_SERVICE_UNAVAILABLE
    int err_lsapi_conn_acquire;   // default HTTP_SERVICE_UNAVAILABLE
    int err_lsapi_conn_determine; // default HTTP_SERVICE_UNAVAILABLE

    int err_backend_nohdrs;       // default HTTP_SERVICE_UNAVAILABLE
    int err_backend_endhdrs;      // default HTTP_SERVICE_UNAVAILABLE (Premature end of headers)
    int err_backend_sendreq;      // default HTTP_SERVICE_UNAVAILABLE
    int err_backend_recvhdr;      // default HTTP_SERVICE_UNAVAILABLE
    int err_backend_recvrsp;      // default HTTP_SERVICE_UNAVAILABLE
    int err_backend_connect;      // default HTTP_SERVICE_UNAVAILABLE
    int err_backend_reject;       // default HTTP_INSUFFICIENT_STORAGE

    int err_client_setup;         // default HTTP_BAD_REQUEST

    const char *path_regex;

    unsigned mod_php_behaviour_off: 1;
    unsigned engine_off: 1;
    unsigned measure_time: 1;
    unsigned process_phpini: 1;
    unsigned output_buffering: 1;
    unsigned keep_http200: 1;

    unsigned mod_php_behaviour_off_was_set: 1;
    unsigned engine_off_was_set: 1;
    unsigned measure_time_was_set: 1;
    unsigned process_phpini_was_set: 1;
    unsigned output_buffering_was_set: 1;
    unsigned keep_http200_was_set: 1;

    unsigned err_server_docroot_was_set: 1;
    unsigned err_server_uid_was_set: 1;
    unsigned err_script_perms_was_set: 1;

    unsigned err_lsapi_create_was_set: 1;
    unsigned err_lsapi_internal_was_set: 1;
    unsigned err_lsapi_init_was_set: 1;
    unsigned err_lsapi_init_util_was_set: 1;
    unsigned err_lsapi_conn_acquire_was_set: 1;
    unsigned err_lsapi_conn_determine_was_set: 1;

    unsigned err_backend_nohdrs_was_set: 1;
    unsigned err_backend_endhdrs_was_set: 1;
    unsigned err_backend_sendreq_was_set: 1;
    unsigned err_backend_recvhdr_was_set: 1;
    unsigned err_backend_recvrsp_was_set: 1;
    unsigned err_backend_connect_was_set: 1;
    unsigned err_backend_reject_was_set: 1;

    unsigned err_client_setup_was_set: 1;
/* LSAPI_PRO_ONLY:BEGIN */

    unsigned dump_request_info: 1;
    unsigned dump_request_info_was_set: 1;
/* LSAPI_PRO_ONLY:END */
};
typedef struct lsapi_dir_conf_t lsapi_dir_conf_t;


/* LSAPI_PRO_ONLY:BEGIN */
struct dump_request_info_t {
    int http_status_code;
    const char *backend_path;
    uint32_t real_uid;
    uint32_t real_gid;
    uint32_t lve_uid;
    const char *socket_path;
    const char *phprc;
    unsigned debug_enabled;
    unsigned accept_notify;
    const char *domain;
    const char *tmpdir;
    unsigned contentLength;
    const lscapi_var_t *envs;
    int envNum;
    const lscapi_var_t *specialEnvs;
    int specialEnvNum;
    const char *criu_socket_path;
    const char *criu_imgs_dir_path;
    struct timeval create_connection_tv;
    struct timeval get_response_tv;
};
typedef struct dump_request_info_t dump_request_info_t;

// To intermediately write request portion of dump_info into r->notes
extern const char lscapi_request_info_key[];
extern const char troubleshooting[];

void lsapi_dump_request_info(request_rec *r, const dump_request_info_t *info);
/* LSAPI_PRO_ONLY:END */

const char* lsapi_get_status_line(int status);

lsapi_svr_conf_t *lsapi_get_svr_config(server_rec *s);

int lscapi_util_init(server_rec *main_server, apr_pool_t *config_pool, char *errbuf, size_t errlen);
//apr_status_t lscapi_util_child_init(apr_pool_t *configpool, server_rec *s);

void* lsapi_alloc_from_pool(size_t sz, void *user_data);

lscapi_rec *lscapi_create_connection(request_rec *r, const char *backend_path,
                                       unsigned *flagsPtr, apr_status_t *statusPtr,
                                       char *errbuf, size_t errlen,
                                       const module *m);

int lscapi_parse_server_vars(request_rec *r, lscapi_var_t **varsPtr);
int lscapi_parse_special_vars(request_rec *r, lsapi_dir_conf_t *cfg, lscapi_var_t **varsPtr);

void lscapi_recreate_http_header(request_rec *r,
                                lsapi_http_header_index_t **hdrIndexPtr,
                                lsapi_header_offset_t **hdrOffsetsPtr,
                                size_t *hdrOffsetsNumPtr,
                                size_t *contentLengthPtr,
                                char **bufPtr, size_t *bufLenPtr);

#ifdef lscapi_rlog
#undef lscapi_rlog
#endif

#define lscapi_rlog(level, status, r, fmt, ...)           \
    lscapi_log_rerror(__FILE__, __LINE__, level, status, r, fmt, ##__VA_ARGS__)


void lscapi_log_rerror(const char *file, int line, int level,
                       apr_status_t status,
                        const request_rec *r, const char *fmt, ...)
                        __attribute__((format(printf,6,7)));

#ifdef lscapi_log
#undef lscapi_log
#endif

#define lscapi_log(level, status, s, fmt, ...)           \
    lscapi_log_error(__FILE__, __LINE__, level, status, s, fmt, ##__VA_ARGS__)


void lscapi_log_error(const char *file, int line, int level,
                       apr_status_t status,
                        const server_rec *s, const char *fmt, ...)
                        __attribute__((format(printf,6,7)));

#ifdef lscapi_notice
#undef lscapi_notice
#endif

#define lscapi_notice(lsapi_svr_conf_t, level, s, fmt, ...)                                   \
    do {                                                                                      \
        if((lsapi_svr_conf_t)->debug_enabled) {                                               \
            lscapi_log_error(__FILE__, __LINE__, APLOG_NOTICE, level, s, fmt, ##__VA_ARGS__); \
        }                                                                                     \
    } while(0)


#ifndef WITHOUT_SENTRY
void set_lsapi_sentry(int value);
int get_lsapi_sentry(void);
void lscapi_sentry_log(int level, const char *fmt, const char *buf);
void lscapi_process_sentry_log(void);
#endif

apr_status_t lscapi_do_request(lscapi_rec *lscapi, lsphp_conn_t *backend, request_rec *r,
                               unsigned *eventMaskPtr,
                               lsapi_svr_conf_t *svrcfg, lsapi_dir_conf_t *dircfg);

const char* lscapi_php_value_handler(cmd_parms *cmd, void *cfg,
                                    const char *name, const char *value,
                                    int isAdmin, int valueType);
const char *lscapi_php_flag_handler(cmd_parms *cmd, void *cfg,
                                    const char *name, const char *value,
                                    int isAdmin, int valueType);
void *lscapi_create_dir_config(apr_pool_t *pool, char *x);
void *lscapi_merge_dir_config(apr_pool_t *pool, void *base, void *cur);
void *lscapi_create_svr_config(apr_pool_t *pool, server_rec *s);
void *lscapi_merge_svr_config(apr_pool_t *pool, void *base, void *cur);


apr_status_t lscapi_mutex_register(const char *mutex_type,
                                  apr_pool_t *pconf);

apr_status_t lscapi_mutex_create(apr_global_mutex_t **mutex,
                                const char **lockfile,
                                const char *mutex_type,
                                apr_pool_t *pconf,
                                server_rec *s,
                                lsapi_svr_conf_t *cfg);

#if 0
void lscapi_cleanup_mutex(const char *apache_user);
#endif

apr_status_t lscapi_mutex_fcntl_create(apr_global_mutex_t **mutex,
                                const char **lockfile,
                                const char *mutex_type,
                                apr_pool_t *pconf,
                                server_rec *main_server,
                                lsapi_svr_conf_t *cfg);


void set_use_starter_hash_t_structures(void);

pid_t lscapi_starter_send_spawn_cmd(const spawn_info_t *spawn_info, request_rec *r, int force_start);


apr_status_t lscapi_starter_child_init(server_rec* main_server, apr_pool_t* config_pool, const char *prefix);
apr_status_t lscapi_starter_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp);
apr_status_t lscapi_starter_init(const char *prefix, server_rec *main_server, apr_pool_t *config_pool);


/* LSAPI_PRO_ONLY:BEGIN */
typedef struct lsapi_connslot_t {
    int sock;
    pid_t worker_pid;
    int is_open;
    int is_used;
} lsapi_connslot_t;

typedef struct lsapi_connslot_info_t {
    apr_thread_mutex_t *conn_mutex;
    lsapi_connslot_t *slot;
    int slot_index;
} lsapi_connslot_info_t;

apr_status_t lscapi_connpool_child_init(const char *prefix, server_rec *main_server, apr_pool_t *config_pool, lsapi_svr_conf_t *cfg);
int lscapi_grab_sock_slot(server_rec *s, lsapi_svr_conf_t *cfg, const char *sock_name, lsapi_connslot_info_t *slot_info);
int lscapi_ungrab_sock_slot(server_rec *s, lsapi_connslot_info_t *slot_info);

/* LSAPI_PRO_ONLY:END */
char* lscapi_make_fname_in_logdir(server_rec *s, apr_pool_t *pool, const char *fname);

void lscapi_reset_http_error_state(request_rec *r);

extern const char LSCAPI_MEASURE_REQUEST_GOT[];
extern const char LSCAPI_MEASURE_CONN_ESTABLISHED[];
extern const char LSCAPI_MEASURE_REQUEST_SENT[];
extern const char LSCAPI_MEASURE_HEADER_GOT[];
extern const char LSCAPI_MEASURE_RESPONSE_GOT[];

void lscapi_write_measured_time(request_rec *r, const char *key);
apr_status_t lscapi_get_measured_timedelta(request_rec *r,
                                           const char *key_from, const char *key_to,
                                           struct timeval *tv_delta);

int lsapi_is_server_restart(void);

void lscapi_get_module_signature(char *buf, size_t buf_len);

#endif //_LSCAPI_UTIL_H_

