text: /*
   * Copyright (C) 2021 Red Hat
   * 
   * 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.
   * 
   * 3. Neither the name of the copyright holder nor the names of its contributors may
   * be used to endorse or promote products derived from this software without specific
   * prior written permission.
   * 
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
   */
  
  /* pgagroal */
  #include <pgagroal.h>
  #include <logging.h>
  #include <utils.h>
  
  /* system */
  #include <ev.h>
  #include <execinfo.h>
  #include <pwd.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <termios.h>
  #include <unistd.h>
  #include <openssl/pem.h>
  #include <sys/types.h>
  
  #ifndef EVBACKEND_LINUXAIO
  #define EVBACKEND_LINUXAIO 0x00000040U
  #endif
  
  #ifndef EVBACKEND_IOURING
  #define EVBACKEND_IOURING  0x00000080U
  #endif
  
  extern char** environ;
  #ifdef HAVE_LINUX
  static bool env_changed = false;
  static int max_process_title_size = -1;
  #endif
  
  int32_t
  pgagroal_get_request(struct message* msg)
  {
     if (msg == NULL || msg->data == NULL || msg->length < 8)
     {
        return -1;
     }
  
     return pgagroal_read_int32(msg->data + 4);
  }
  
  int
  pgagroal_extract_username_database(struct message* msg, char** username, char** database, char** appname)
  {
     int start, end;
     int counter = 0;
     signed char c;
     char** array = NULL;
     size_t size;
     char* un = NULL;
     char* db = NULL;
     char* an = NULL;
  
     *username = NULL;
     *database = NULL;
     *appname = NULL;
  
     /* We know where the parameters start, and we know that the message is zero terminated */
     for (int i = 8; i < msg->length - 1; i++)
     {
        c = pgagroal_read_byte(msg->data + i);
        if (c == 0)
           counter++;
     }
  
     array = (char**)malloc(sizeof(char*) * counter);
  
     counter = 0;
     start = 8;
     end = 8;
  
     for (int i = 8; i < msg->length - 1; i++)
     {
        c = pgagroal_read_byte(msg->data + i);
        end++;
        if (c == 0)
        {
           array[counter] = (char*)malloc(end - start);
           memset(array[counter], 0, end - start);
           memcpy(array[counter], msg->data + start, end - start);
                 
           start = end;
           counter++;
        }
     }
           
     for (int i = 0; i < counter; i++)
     {
        if (!strcmp(array[i], "user"))
        {
           size = strlen(array[i + 1]) + 1;
           un = malloc(size);
           memset(un, 0, size);
           memcpy(un, array[i + 1], size);
  
           *username = un;
        }
        else if (!strcmp(array[i], "database"))
        {
           size = strlen(array[i + 1]) + 1;
           db = malloc(size);
           memset(db, 0, size);
           memcpy(db, array[i + 1], size);
  
           *database = db;
        }
        else if (!strcmp(array[i], "application_name"))
        {
           size = strlen(array[i + 1]) + 1;
           an = malloc(size);
           memset(an, 0, size);
           memcpy(an, array[i + 1], size);
  
           *appname = an;
        }
     }
  
     if (*database == NULL)
        *database = *username;
  
     pgagroal_log_trace("Username: %s", *username);
     pgagroal_log_trace("Database: %s", *database);
  
     for (int i = 0; i < counter; i++)
        free(array[i]);
     free(array);
  
     return 0;
  }
  
  int
  pgagroal_extract_message(char type, struct message* msg, struct message** extracted)
  {
     int offset;
     int m_length;
     void* data = NULL;
     struct message* result = NULL;
  
     offset = 0;
     *extracted = NULL;
  
     while (result == NULL && offset < msg->length)
     {
        char t = (char)pgagroal_read_byte(msg->data + offset);
  
        if (type == t)
        {
           m_length = pgagroal_read_int32(msg->data + offset + 1);
  
           result = (struct message*)malloc(sizeof(struct message));
           data = (void*)malloc(1 + m_length);
  
           memcpy(data, msg->data + offset, 1 + m_length);
  
           result->kind = pgagroal_read_byte(data);
           result->length = 1 + m_length;
           result->max_length = 1 + m_length;
           result->data = data;
  
           *extracted = result;
  
           return 0;
        }
        else
        {
           offset += 1;
           offset += pgagroal_read_int32(msg->data + offset);
        }
     }
  
     return 1;
  }
  
  int
  pgagroal_extract_error_message(struct message* msg, char** error)
  {
     int max = 0;
     int offset = 5;
     signed char type;
     char* s = NULL;
     char* result = NULL;
  
     *error = NULL;
  
     if (msg->kind == 'E')
     {
        max = pgagroal_read_int32(msg->data + 1);
  
        while (result == NULL && offset < max)
        {
           type = pgagroal_read_byte(msg->data + offset);
           s = pgagroal_read_string(msg->data + offset + 1);
  
           if (type == 'M')
           {
              result = (char*)malloc(strlen(s) + 1);
              memset(result, 0, strlen(s) + 1);
              memcpy(result, s, strlen(s));
  
              *error = result;
           }
  
           offset += 1 + strlen(s) + 1;
        }
     }
     else
     {
        goto error;
     }
  
     return 0;
  
  error:
  
     return 1;
  }
  
  char*
  pgagroal_get_state_string(signed char state)
  {
     switch (state)
     {
        case STATE_NOTINIT:
           return "Not initialized";
        case STATE_INIT:
           return "Initializing";
        case STATE_FREE:
           return "Free";
        case STATE_IN_USE:
           return "Active";
        case STATE_GRACEFULLY:
           return "Graceful";
        case STATE_FLUSH:
           return "Flush";
        case STATE_IDLE_CHECK:
           return "Idle check";
        case STATE_VALIDATION:
           return "Validating";
        case STATE_REMOVE:
           return "Removing";
     }
  
     return "Unknown";
  }
  
  signed char
  pgagroal_read_byte(void* data)
  {
     return (signed char) *((char*)data);
  }
  
  int16_t
  pgagroal_read_int16(void* data)
  {
     unsigned char bytes[] = {*((unsigned char*)data),
                              *((unsigned char*)(data + 1))};
  
     int16_t res = (int16_t)((bytes[0] << 8)) |
                            ((bytes[1]     ));
  
     return res;
  }
  
  int32_t
  pgagroal_read_int32(void* data)
  {
     unsigned char bytes[] = {*((unsigned char*)data),
                              *((unsigned char*)(data + 1)),
                              *((unsigned char*)(data + 2)),
                              *((unsigned char*)(data + 3))};
  
     int32_t res = (int32_t)((bytes[0] << 24)) |
                            ((bytes[1] << 16)) |
                            ((bytes[2] <<  8)) |
                            ((bytes[3]      ));
  
     return res;
  }
  
  long
  pgagroal_read_long(void* data)
  {
     unsigned char bytes[] = {*((unsigned char*)data),
                              *((unsigned char*)(data + 1)),
                              *((unsigned char*)(data + 2)),
                              *((unsigned char*)(data + 3)),
                              *((unsigned char*)(data + 4)),
                              *((unsigned char*)(data + 5)),
                              *((unsigned char*)(data + 6)),
                              *((unsigned char*)(data + 7))};
  
     long res = (long)(((long)bytes[0]) << 56) |
                      (((long)bytes[1]) << 48) |
                      (((long)bytes[2]) << 40) |
                      (((long)bytes[3]) << 32) |
                      (((long)bytes[4]) << 24) |
                      (((long)bytes[5]) << 16) |
                      (((long)bytes[6]) <<  8) |
                      (((long)bytes[7])      );
  
     return res;
  }
  
  char*
  pgagroal_read_string(void* data)
  {
     return (char*)data;
  }
  
  
  void
  pgagroal_write_byte(void* data, signed char b)
  {
     *((char*)(data)) = b;
  }
  
  void
  pgagroal_write_int32(void* data, int32_t i)
  {
     char *ptr = (char*)&i;
  
     *((char*)(data + 3)) = *ptr;
     ptr++;
     *((char*)(data + 2)) = *ptr;
     ptr++;
     *((char*)(data + 1)) = *ptr;
     ptr++;
     *((char*)(data)) = *ptr;
  }
  
  void
  pgagroal_write_long(void* data, long l)
  {
     char *ptr = (char*)&l;
  
     *((char*)(data + 7)) = *ptr;
     ptr++;
     *((char*)(data + 6)) = *ptr;
     ptr++;
     *((char*)(data + 5)) = *ptr;
     ptr++;
     *((char*)(data + 4)) = *ptr;
     ptr++;
     *((char*)(data + 3)) = *ptr;
     ptr++;
     *((char*)(data + 2)) = *ptr;
     ptr++;
     *((char*)(data + 1)) = *ptr;
     ptr++;
     *((char*)(data)) = *ptr;
  }
  
  void
  pgagroal_write_string(void* data, char* s)
  {
     memcpy(data, s, strlen(s));
  }
  
  bool
  pgagroal_bigendian(void)
  {
     short int word = 0x0001;
     char *b = (char *)&word;
     return (b[0] ? false : true);
  }
  
  unsigned int
  pgagroal_swap(unsigned int i)
  {
     return ((i << 24) & 0xff000000) |
            ((i << 8)  & 0x00ff0000) |
            ((i >> 8)  & 0x0000ff00) |
            ((i >> 24) & 0x000000ff);
  }
  
  void
  pgagroal_libev_engines(void)
  {
     unsigned int engines = ev_supported_backends();
  
     if (engines & EVBACKEND_SELECT)
     {
        pgagroal_log_debug("libev available: select");
     }
     if (engines & EVBACKEND_POLL)
     {
        pgagroal_log_debug("libev available: poll");
     }
     if (engines & EVBACKEND_EPOLL)
     {
        pgagroal_log_debug("libev available: epoll");
     }
     if (engines & EVBACKEND_LINUXAIO)
     {
        pgagroal_log_debug("libev available: linuxaio");
     }
     if (engines & EVBACKEND_IOURING)
     {
        pgagroal_log_debug("libev available: iouring");
     }
     if (engines & EVBACKEND_KQUEUE)
     {
        pgagroal_log_debug("libev available: kqueue");
     }
     if (engines & EVBACKEND_DEVPOLL)
     {
        pgagroal_log_debug("libev available: devpoll");
     }
     if (engines & EVBACKEND_PORT)
     {
        pgagroal_log_debug("libev available: port");
     }
  }
  
  unsigned int
  pgagroal_libev(char* engine)
  {
     unsigned int engines = ev_supported_backends();
  
     if (engine)
     {
        if (!strcmp("select", engine))
        {
           if (engines & EVBACKEND_SELECT)
           {
              return EVBACKEND_SELECT;
           }
           else
           {
              pgagroal_log_warn("libev not available: select");
           }
        }
        else if (!strcmp("poll", engine))
        {
           if (engines & EVBACKEND_POLL)
           {
              return EVBACKEND_POLL;
           }
           else
           {
              pgagroal_log_warn("libev not available: poll");
           }
        }
        else if (!strcmp("epoll", engine))
        {
           if (engines & EVBACKEND_EPOLL)
           {
              return EVBACKEND_EPOLL;
           }
           else
           {
              pgagroal_log_warn("libev not available: epoll");
           }
        }
        else if (!strcmp("linuxaio", engine))
        {
           return EVFLAG_AUTO;
        }
        else if (!strcmp("iouring", engine))
        {
           if (engines & EVBACKEND_IOURING)
           {
              return EVBACKEND_IOURING;
           }
           else
           {
              pgagroal_log_warn("libev not available: iouring");
           }
        }
        else if (!strcmp("devpoll", engine))
        {
           if (engines & EVBACKEND_DEVPOLL)
           {
              return EVBACKEND_DEVPOLL;
           }
           else
           {
              pgagroal_log_warn("libev not available: devpoll");
           }
        }
        else if (!strcmp("port", engine))
        {
           if (engines & EVBACKEND_PORT)
           {
              return EVBACKEND_PORT;
           }
           else
           {
              pgagroal_log_warn("libev not available: port");
           }
        }
        else if (!strcmp("auto", engine) || !strcmp("", engine))
        {
           return EVFLAG_AUTO;
        }
        else
        {
           pgagroal_log_warn("libev unknown option: %s", engine);
        }
     }
  
     return EVFLAG_AUTO;
  }
  
  char*
  pgagroal_libev_engine(unsigned int val)
  {
     switch (val)
     {
        case EVBACKEND_SELECT:
           return "select";
        case EVBACKEND_POLL:
           return "poll";
        case EVBACKEND_EPOLL:
           return "epoll";
        case EVBACKEND_LINUXAIO:
           return "linuxaio";
        case EVBACKEND_IOURING:
           return "iouring";
        case EVBACKEND_KQUEUE:
           return "kqueue";
        case EVBACKEND_DEVPOLL:
           return "devpoll";
        case EVBACKEND_PORT:
           return "port";
     }
  
     return "Unknown";
  }
  
  char*
  pgagroal_get_home_directory(void)
  {
     struct passwd *pw = getpwuid(getuid());
  
     if (pw == NULL)
     {
        return NULL;
     }
  
     return pw->pw_dir;
  }
  
  char*
  pgagroal_get_user_name(void)
  {
     struct passwd *pw = getpwuid(getuid());
  
     if (pw == NULL)
     {
        return NULL;
     }
  
     return pw->pw_name;
  }
  
  char*
  pgagroal_get_password(void)
  {
     char p[MAX_PASSWORD_LENGTH];
     struct termios oldt, newt;
     int i = 0;
     int c;
     char* result = NULL;
  
     memset(&p, 0, sizeof(p));
  
     tcgetattr(STDIN_FILENO, &oldt);
     newt = oldt;
  
     newt.c_lflag &= ~(ECHO);
  
     tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  
     while ((c = getchar()) != '\n' && c != EOF && i < MAX_PASSWORD_LENGTH)
     {
        p[i++] = c;
     }
     p[i] = '\0';
  
     tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  
     result = malloc(strlen(p) + 1);
     memset(result, 0, strlen(p) + 1);
  
     memcpy(result, &p, strlen(p));
  
     return result;
  }
  
  int
  pgagroal_base64_encode(char* raw, int raw_length, char** encoded)
  {
     BIO* b64_bio;
     BIO* mem_bio;
     BUF_MEM* mem_bio_mem_ptr;
     char* r = NULL;
  
     if (raw == NULL)
     {
        goto error;
     }
  
     b64_bio = BIO_new(BIO_f_base64());
     mem_bio = BIO_new(BIO_s_mem());
  
     BIO_push(b64_bio, mem_bio);
     BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);
     BIO_write(b64_bio, raw, raw_length);
     BIO_flush(b64_bio);
  
     BIO_get_mem_ptr(mem_bio, &mem_bio_mem_ptr);
  
     BIO_set_close(mem_bio, BIO_NOCLOSE);
     BIO_free_all(b64_bio);
  
     BUF_MEM_grow(mem_bio_mem_ptr, (*mem_bio_mem_ptr).length + 1);
     (*mem_bio_mem_ptr).data[(*mem_bio_mem_ptr).length] = '\0';
  
     r = malloc(strlen((*mem_bio_mem_ptr).data) + 1);
     memset(r, 0, strlen((*mem_bio_mem_ptr).data) + 1);
     memcpy(r, (*mem_bio_mem_ptr).data, strlen((*mem_bio_mem_ptr).data));
  
     BUF_MEM_free(mem_bio_mem_ptr);
  
     *encoded = r;
  
     return 0;
  
  error:
  
     *encoded = NULL;
  
     return 1;
  }
  
  int
  pgagroal_base64_decode(char* encoded, size_t encoded_length, char** raw, int* raw_length)
  {
     BIO* b64_bio;
     BIO* mem_bio;
     size_t size;
     char* decoded;
     int index;
  
     if (encoded == NULL)
     {
        goto error;
     }
  
     size = (encoded_length * 3) / 4 + 1;
     decoded = malloc(size);
     memset(decoded, 0, size);
  
     b64_bio = BIO_new(BIO_f_base64());
     mem_bio = BIO_new(BIO_s_mem());
  
     BIO_write(mem_bio, encoded, encoded_length);
     BIO_push(b64_bio, mem_bio);
     BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);
  
     index = 0;
     while (0 < BIO_read(b64_bio, decoded + index, 1) )
     {
        index++;
     }
  
     BIO_free_all(b64_bio);
  
     *raw = decoded;
     *raw_length = index;
  
     return 0;
  
  error:
  
     *raw = NULL;
     *raw_length = 0;
  
     return 1;
  }
  
  void
  pgagroal_set_proc_title(int argc, char** argv, char* s1, char *s2)
  {
  #ifdef HAVE_LINUX
     char title[256];
     size_t size;
     char** env = environ;
     int es = 0;
  
     if (!env_changed)
     {
        for (int i = 0; env[i] != NULL; i++)
        {
           es++;
        }
  
        environ = (char**)malloc(sizeof(char*) * (es + 1));
        if (environ == NULL)
        {
           return;
        }
  
        for (int i = 0; env[i] != NULL; i++)
        {
           size = strlen(env[i]);
           environ[i] = (char*)malloc(size + 1);
  
           if (environ[i] == NULL)
           {
              return;
           }
  
           memset(environ[i], 0, size + 1);
           memcpy(environ[i], env[i], size);
        }
        environ[es] = NULL;
        env_changed = true;
     }
  
     if (max_process_title_size == -1)
     {
        int m = 0;
  
        for (int i = 0; i < argc; i++)
        {
           m += strlen(argv[i]);
           m += 1;
        }
  
        max_process_title_size = m;
        memset(*argv, 0, max_process_title_size);
     }
  
     memset(&title, 0, sizeof(title));
  
     if (s1 != NULL && s2 != NULL)
     {
        snprintf(title, sizeof(title) - 1, "pgagroal: %s/%s", s1, s2);
     }
     else
     {
        snprintf(title, sizeof(title) - 1, "pgagroal: %s", s1);
     }
  
     size = MIN(max_process_title_size, sizeof(title));
     size = MIN(size, strlen(title) + 1);
  
     memcpy(*argv, title, size);
     memset(*argv + size, 0, 1);
  
  #else
     if (s1 != NULL && s2 != NULL)
     {
        setproctitle("-pgagroal: %s/%s", s1, s2);
     }
     else
     {
        setproctitle("-pgagroal: %s", s1);
     }
  #endif
  }
  
  #ifdef DEBUG
  
  int
  pgagroal_backtrace(void)
  {
  #ifdef HAVE_LINUX
     void* array[100];
     size_t size;
     char** strings;
  
     size = backtrace(array, 100);
     strings = backtrace_symbols(array, size);
  
     for (size_t i = 0; i < size; i++)
     {
        printf("%s\n", strings[i]);
     }
  
     free(strings);
  #endif
  
     return 0;
  }
  
  #endif
  