#ifndef SQL_USER_CACHE_INCLUDED
#define SQL_USER_CACHE_INCLUDED

/* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License, version 2.0,
   as published by the Free Software Foundation.

   This program is also distributed with certain software (including
   but not limited to OpenSSL) that is licensed under separate terms,
   as designated in a particular file or component or in included license
   documentation.  The authors of MySQL hereby grant you an additional
   permission to link the program and your derivative works with the
   separately licensed software that they have included with MySQL.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License, version 2.0, for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */

#include "my_global.h"                  // NO_EMBEDDED_ACCESS_CHECKS
#include "my_sys.h"                     // wild_many, wild_one, wild_prefix
#include <string.h>                     // strchr
#include "mysql_com.h"                  // SCRAMBLE_LENGTH
#include "violite.h"                    // SSL_type
#include "hash_filo.h"                  // HASH, hash_filo
#include "records.h"                    // READ_RECORD
#include "partitioned_rwlock.h"         // Partitioned_rwlock

#include "prealloced_array.h"

/* Forward Declarations */
class String;

/* Classes */

class ACL_HOST_AND_IP
{
  char *hostname;
  size_t hostname_length;
  long ip, ip_mask; // Used with masked ip:s

  const char *calc_ip(const char *ip_arg, long *val, char end);

public:
  const char *get_host() const { return hostname ? hostname : ""; }
  size_t get_host_len() const { return hostname_length; }

  bool has_wildcard()
  {
    return (strchr(hostname,wild_many) ||
            strchr(hostname,wild_one)  || ip_mask );
  }

  bool check_allow_all_hosts()
  {
    return (!hostname ||
            (hostname[0] == wild_many && !hostname[1]));
  }

  void update_hostname(const char *host_arg);
  bool compare_hostname(const char *host_arg, const char *ip_arg);
  bool is_null() const { return hostname == NULL; }
};

class ACL_ACCESS {
public:
  ACL_HOST_AND_IP host;
  ulong sort;
  ulong access;
};

/* ACL_HOST is used if no host is specified */

class ACL_HOST :public ACL_ACCESS
{
public:
  char *db;
};

class ACL_USER :public ACL_ACCESS
{
public:
  USER_RESOURCES user_resource;
  char *user;
  /**
    The salt variable is used as the password hash for
    native_password_authetication.
  */
  uint8 salt[SCRAMBLE_LENGTH + 1];       // scrambled password in binary form
  /**
    In the old protocol the salt_len indicated what type of autnetication
    protocol was used: 0 - no password, 4 - 3.20, 8 - 4.0,  20 - 4.1.1
  */
  uint8 salt_len;
  enum SSL_type ssl_type;
  const char *ssl_cipher, *x509_issuer, *x509_subject;
  LEX_CSTRING plugin;
  LEX_STRING auth_string;
  bool password_expired;
  bool can_authenticate;
  MYSQL_TIME password_last_changed;
  uint password_lifetime;
  bool use_default_password_lifetime;
  /**
    Specifies whether the user account is locked or unlocked.
  */
  bool account_locked;

  ACL_USER *copy(MEM_ROOT *root);
};

class ACL_DB :public ACL_ACCESS
{
public:
  char *user,*db;
};

class ACL_PROXY_USER :public ACL_ACCESS
{
  const char *user;
  ACL_HOST_AND_IP proxied_host;
  const char *proxied_user;
  bool with_grant;

  typedef enum { 
    MYSQL_PROXIES_PRIV_HOST, 
    MYSQL_PROXIES_PRIV_USER, 
    MYSQL_PROXIES_PRIV_PROXIED_HOST,
    MYSQL_PROXIES_PRIV_PROXIED_USER, 
    MYSQL_PROXIES_PRIV_WITH_GRANT,
    MYSQL_PROXIES_PRIV_GRANTOR,
    MYSQL_PROXIES_PRIV_TIMESTAMP } old_acl_proxy_users;
public:
  ACL_PROXY_USER () {};

  void init(const char *host_arg, const char *user_arg,
            const char *proxied_host_arg, const char *proxied_user_arg,
            bool with_grant_arg);

  void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
            const char *proxied_host_arg, const char *proxied_user_arg,
            bool with_grant_arg);

  void init(TABLE *table, MEM_ROOT *mem);

  bool get_with_grant() { return with_grant; }
  const char *get_user() { return user; }
  const char *get_proxied_user() { return proxied_user; }
  const char *get_proxied_host() { return proxied_host.get_host(); }
  void set_user(MEM_ROOT *mem, const char *user_arg) 
  { 
    user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
  }

  void check_validity(bool check_no_resolve);

  bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
                const char *proxied_user_arg, bool any_proxy_user);

  inline static bool auth_element_equals(const char *a, const char *b)
  {
    return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
  }


  bool pk_equals(ACL_PROXY_USER *grant);

  bool granted_on(const char *host_arg, const char *user_arg)
  {
    return (((!user && (!user_arg || !user_arg[0])) ||
             (user && user_arg && !strcmp(user, user_arg))) &&
            ((host.is_null() && (!host_arg || !host_arg[0])) ||
             (!host.is_null() && host_arg && !strcmp(host.get_host(), host_arg))));
  }


  void print_grant(String *str);

  void set_data(ACL_PROXY_USER *grant)
  {
    with_grant= grant->with_grant;
  }

  static int store_pk(TABLE *table,
                      const LEX_CSTRING &host,
                      const LEX_CSTRING &user,
                      const LEX_CSTRING &proxied_host,
                      const LEX_CSTRING &proxied_user);

  static int store_with_grant(TABLE * table,
                              bool with_grant);

  static int store_data_record(TABLE *table,
                               const LEX_CSTRING &host,
                               const LEX_CSTRING &user,
                               const LEX_CSTRING &proxied_host,
                               const LEX_CSTRING &proxied_user,
                               bool with_grant,
                               const char *grantor);
};

#ifndef NO_EMBEDDED_ACCESS_CHECKS

class acl_entry :public hash_filo_element
{
public:
  ulong access;
  uint16 length;
  char key[1];                          // Key will be stored here
};


class GRANT_COLUMN :public Sql_alloc
{
public:
  char *column;
  ulong rights;
  size_t key_length;
  GRANT_COLUMN(String &c,  ulong y);
};


class GRANT_NAME :public Sql_alloc
{
public:
  ACL_HOST_AND_IP host;
  char *db, *user, *tname, *hash_key;
  ulong privs;
  ulong sort;
  size_t key_length;
  GRANT_NAME(const char *h, const char *d,const char *u,
             const char *t, ulong p, bool is_routine);
  GRANT_NAME (TABLE *form, bool is_routine);
  virtual ~GRANT_NAME() {};
  virtual bool ok() { return privs != 0; }
  void set_user_details(const char *h, const char *d,
                        const char *u, const char *t,
                        bool is_routine);
};


class GRANT_TABLE :public GRANT_NAME
{
public:
  ulong cols;
  HASH hash_columns;

  GRANT_TABLE(const char *h, const char *d,const char *u,
              const char *t, ulong p, ulong c);
  explicit GRANT_TABLE(TABLE *form);
  bool init(TABLE *col_privs);
  ~GRANT_TABLE();
  bool ok() { return privs != 0 || cols != 0; }
};


#endif /* NO_EMBEDDED_ACCESS_CHECKS */


/* Data Structures */

#ifndef NO_EMBEDDED_ACCESS_CHECKS
extern MEM_ROOT global_acl_memory;
extern MEM_ROOT memex; 
extern bool initialized;
const size_t ACL_PREALLOC_SIZE = 10U;
extern Prealloced_array<ACL_USER, ACL_PREALLOC_SIZE> *acl_users;
extern Prealloced_array<ACL_PROXY_USER, ACL_PREALLOC_SIZE> *acl_proxy_users;
extern Prealloced_array<ACL_DB, ACL_PREALLOC_SIZE> *acl_dbs;
extern Prealloced_array<ACL_HOST_AND_IP, ACL_PREALLOC_SIZE> *acl_wild_hosts;
extern HASH column_priv_hash, proc_priv_hash, func_priv_hash;
extern hash_filo *acl_cache;
extern HASH acl_check_hosts;
extern bool allow_all_hosts;
extern uint grant_version; /* Version of priv tables */
extern Partitioned_rwlock LOCK_grant;

GRANT_NAME *name_hash_search(HASH *name_hash,
                             const char *host,const char* ip,
                             const char *db,
                             const char *user, const char *tname,
                             bool exact, bool name_tolower);

inline GRANT_NAME * routine_hash_search(const char *host, const char *ip,
                                        const char *db, const char *user,
                                        const char *tname, bool proc,
                                        bool exact)
{
  return (GRANT_TABLE*)
    name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
                     host, ip, db, user, tname, exact, TRUE);
}

inline GRANT_TABLE * table_hash_search(const char *host, const char *ip,
                                       const char *db, const char *user,
                                       const char *tname, bool exact)
{
  return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
                                         user, tname, exact, FALSE);
}

inline GRANT_COLUMN * column_hash_search(GRANT_TABLE *t, const char *cname,
                                         size_t length)
{
  return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
                                        (uchar*) cname, length);
}


#endif /* NO_EMBEDDED_ACCESS_CHECKS */

#endif /* SQL_USER_CACHE_INCLUDED */
