/*
 * Copyright (C) 1996-2026 The Squid Software Foundation and contributors
 *
 * Squid software is distributed under GPLv2+ license and includes
 * contributions from numerous individuals and organizations.
 * Please see the COPYING and CONTRIBUTORS files for details.
 */

#ifndef SQUID_SRC_ACL_ACL_H
#define SQUID_SRC_ACL_ACL_H

#include "acl/forward.h"
#include "defines.h"
#include "dlink.h"
#include "sbuf/SBuf.h"

#include <algorithm>
#include <optional>
#include <ostream>

namespace Acl {

/// the ACL type name known to admins
using TypeName = const char *;
/// a "factory" function for making Acl::Node objects (of some Node child type)
using Maker = Node *(*)(TypeName typeName);
/// use the given Acl::Node Maker for all ACLs of the named type
void RegisterMaker(TypeName typeName, Maker maker);

/// Validate and store the ACL key parameter for ACL types
/// declared using "acl aclname type key argument..." declaration that
/// require unique key values (if any) for each aclname+type combination.
/// Key comparison is case-insensitive.
void SetKey(SBuf &keyStorage, const char *keyParameterName, const char *newKey);

}  // namespace Acl

/// \ingroup ACLAPI
typedef enum {
    // Authorization ACL result states
    ACCESS_DENIED,
    ACCESS_ALLOWED,
    ACCESS_DUNNO,

    // Authentication Acl::Node result states
    ACCESS_AUTH_REQUIRED,    // Missing Credentials
} aclMatchCode;

/// \ingroup ACLAPI
/// Acl::Node check answer
namespace Acl {

class Answer
{
public:
    // TODO: Find a good way to avoid implicit conversion (without explicitly
    // casting every ACCESS_ argument in implicit constructor calls).
    Answer(const aclMatchCode aCode, int aKind = 0): code(aCode), kind(aKind) {}

    Answer() = default;

    bool operator ==(const aclMatchCode aCode) const {
        return code == aCode;
    }

    bool operator !=(const aclMatchCode aCode) const {
        return !(*this == aCode);
    }

    bool operator ==(const Answer &allow) const {
        return code == allow.code && kind == allow.kind;
    }

    operator aclMatchCode() const {
        return code;
    }

    /// Whether an "allow" rule matched. If in doubt, use this popular method.
    /// Also use this method to treat exceptional ACCESS_DUNNO and
    /// ACCESS_AUTH_REQUIRED outcomes as if a "deny" rule matched.
    /// See also: denied().
    bool allowed() const { return code == ACCESS_ALLOWED; }

    /// Whether a "deny" rule matched. Avoid this rarely used method.
    /// Use this method (only) to treat exceptional ACCESS_DUNNO and
    /// ACCESS_AUTH_REQUIRED outcomes as if an "allow" rule matched.
    /// See also: allowed().
    bool denied() const { return code == ACCESS_DENIED; }

    /// whether Squid is uncertain about the allowed() or denied() answer
    bool conflicted() const { return !allowed() && !denied(); }

    /// describes the ACL that was evaluated last while obtaining this answer (for debugging)
    const SBuf &lastCheckDescription() const;

    aclMatchCode code = ACCESS_DUNNO; ///< ACCESS_* code

    /// the matched custom access list verb (or zero)
    int kind = 0;

    /// whether we were computed by the "negate the last explicit action" rule
    bool implicit = false;

    /// the name of the ACL (if any) that was evaluated last while obtaining this answer
    std::optional<SBuf> lastCheckedName;
};

inline std::ostream &
operator <<(std::ostream &o, const Answer &a)
{
    switch (a) {
    case ACCESS_DENIED:
        o << "DENIED";
        break;
    case ACCESS_ALLOWED:
        o << "ALLOWED";
        break;
    case ACCESS_DUNNO:
        o << "DUNNO";
        break;
    case ACCESS_AUTH_REQUIRED:
        o << "AUTH_REQUIRED";
        break;
    }
    return o;
}

/// report the given list of "acl" directives (using squid.conf syntax)
void DumpNamedAcls(std::ostream &, const char *directiveName, NamedAcls *);

/// delete the given list of "acl" directives
void FreeNamedAcls(NamedAcls **);

} // namespace Acl

/// \ingroup ACLAPI
class acl_proxy_auth_match_cache
{
    MEMPROXY_CLASS(acl_proxy_auth_match_cache);

public:
    acl_proxy_auth_match_cache(int matchRv, void * aclData) :
        matchrv(matchRv),
        acl_data(aclData)
    {}

    dlink_node link;
    int matchrv;
    void *acl_data;
};

#endif /* SQUID_SRC_ACL_ACL_H */

