/*
 * Copyright (c) 2003-2013
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 *
 * $Id: acs.h 2646 2013-02-27 20:10:40Z brachman $
 */

/*****************************************************************************
 * COPYRIGHT AND PERMISSION NOTICE
 * 
 * Copyright (c) 2001-2003 The Queen in Right of Canada
 * 
 * All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, and/or sell
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, provided that the above copyright notice(s) and this
 * permission notice appear in all copies of the Software and that both the
 * above copyright notice(s) and this permission notice appear in supporting
 * documentation.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE 
 * BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
 * SOFTWARE.
 * 
 * Except as contained in this notice, the name of a copyright holder shall not
 * be used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization of the
 * copyright holder.
 ***************************************************************************/

#ifndef _ACS_H_
#define _ACS_H_

#include "local.h"

#include <setjmp.h>

enum {
  /* This can be overridden using the ACS_POST_BUFFER_LIMIT directive. */
  DEFAULT_ACS_POST_BUFFER_LIMIT  = 65536
};

/*
 * ACL names will be recognized by this prefix.
 * XXX Should be configurable?
 */
#define ACL_NAME_PREFIX				"acl-"
#define ACL_DISABLED_NAME_PREFIX	"disabled-"

#ifndef ACL_DELEGATE_MAX
#define ACL_DELEGATE_MAX		3
#endif

/*
 * This may be redefined if necessary, but take care to rename any
 * existing files.
 * XXX Should be configurable?
 */
#ifndef ACL_INDEX_FILENAME
#define ACL_INDEX_FILENAME	"INDEX"
#endif

typedef struct Identity Identity;
typedef struct Services Services;
typedef struct Service Service;
typedef struct Rule Rule;
typedef struct Precondition Precondition;
typedef struct Predicate Predicate;
typedef struct User_list User_list;
typedef struct User User;
typedef struct Allow Allow;
typedef struct Deny Deny;
typedef struct Grant_attrs Grant_attrs;
#ifdef NOTDEF
typedef struct Acl_map Acl_map;
typedef struct Acl_index Acl_index;
#endif

typedef enum Rule_order {
  ACS_ORDER_ALLOW_THEN_DENY = 0,
  ACS_ORDER_DENY_THEN_ALLOW = 1,
  ACS_ORDER_UNKNOWN         = 2
} Rule_order;

typedef enum {
  ACL_STATUS_DISABLED = 0,
  ACL_STATUS_ENABLED  = 1,
  ACL_STATUS_ERROR    = -1
} Acl_status;

typedef struct Acl_file {
  Acl_status status;
  char *aclname;			/* The name with all prefixes removed. */
  char *naming_context;
  char *path;				/* The name including any prefixes. */
  int sortkey;
} Acl_file;

/* This is the internal representation of an ACL index file. */
typedef struct Acl_index {
  time_t date_created;
  Dsvec *acl_map;
} Acl_index;

/* Each of these is an entry in the ACL index file pointing to a rule file. */
typedef struct Acl_map {
  Acl_status status;
  unsigned int priority;
  char *path;
  char *expires_expr;
  Services *services;
} Acl_map;

struct Grant_attrs {
  char *constraint;
  char *permit_chaining;
  char *pass_credentials;
  char *pass_http_cookie;
  char *permit_caching;
};

/* Internal representation of a rule file. */
typedef struct Acl_rule {
  Acl_status status;		/* Optional in old rules, required in new ones. */
  char *name;				/* Optional, to check against the filename. */
  char *expires_expr;		/* Optional, for setting a lifetime on the rule. */
  Services *services;
  Identity *identities;
  Rule *rules;
  Grant_attrs default_attrs;
} Acl_rule;

struct Services {
  char *shared;
  Service *service;
};

struct Service {
  char *id;
  char *url_pattern;
  char *url_expr;
  char *url_path;		/* Either url_pattern or the value of url_expr. */
  char *rule_uri;		/* If non-null, this service is delegated. */
  Service *next;
};

struct Identity {
  char *id;
  char *iptr;
  char *ident;
  char *selector_expr;
  Identity *next;
};

struct Rule {
  char *id;
  Precondition *precondition;
  Allow *allows;
  Deny *denies;
  char *order_str;
  Rule_order order;
  Grant_attrs default_attrs;
  struct Rule *next;
};

struct Precondition {
  User_list *user_list;
  Predicate *predicate;
};

struct Predicate {
  Ds *expr;
};

struct User_list {
  User *user;
};

struct User {
  char *id;
  char *name;
  User *next;
};

struct Allow {
  char *id;
  Ds *expr;
  Grant_attrs attrs;
  struct Allow *next;
};

struct Deny {
  char *id;
  Ds *expr;
  struct Deny *next;
};

typedef struct Acs_expr_ns_arg {
  char *name;
  Kwv *kwv;
} Acs_expr_ns_arg;

typedef enum {
  ACS_DENIAL_REASON_FIRST_RULE         = 900,
  ACS_DENIAL_REASON_NO_RULE            = 900,
  ACS_DENIAL_REASON_BY_RULE            = 901,
  ACS_DENIAL_REASON_NO_AUTH            = 902,
  ACS_DENIAL_REASON_REVOKED            = 903,
  ACS_DENIAL_REASON_BY_REDIRECT        = 904,
  ACS_DENIAL_REASON_ACK_NEEDED         = 905,
  ACS_DENIAL_REASON_LOW_AUTH           = 906,
  ACS_DENIAL_REASON_BY_SIMPLE_REDIRECT = 907,
  ACS_DENIAL_REASON_CREDENTIALS_LIMIT  = 908,
  ACS_DENIAL_REASON_INACTIVITY         = 909,
  ACS_DENIAL_REASON_UNKNOWN            = 998,
  ACS_DENIAL_REASON_DEFAULT            = 999,
  ACS_DENIAL_REASON_LAST_RULE          = 999
} Acs_denial_reason;

typedef struct Acs_environment {
  Uri *uri;
  char *url;			/* This is accessed frequently; also appears in env */
  int do_eval;			/* If 0, parse but do not evaluate anything */
  Acl_rule *acl_rule;	/* The ACL applicable to the request, if any */
  Dsvec *notices;
  char *redirect_action;	/* Set via the redirect() function */
  Acs_denial_reason redirect_reason;
  Var_ns *namespaces;
  int trace_level;
  Credentials *credentials;
  int got_jmp_env;
  jmp_buf jmp_env;
  int is_dacsexpr;
} Acs_environment;

typedef struct Acl_url_match {
  int nsegs;
  int nsegs_matched;
  int exact;
  int delegated;
  char *subordinate;
  Acl_rule *acl;
  Service *s;
} Acl_url_match;

typedef enum {
  ACS_HANDLER_URL       = 1,
  ACS_HANDLER_LOCAL_URL = 2,
  ACS_HANDLER_DEFAULT   = 3,
  ACS_HANDLER_REASON    = 4,
  ACS_HANDLER_MESSAGE   = 5,
  ACS_HANDLER_EXPR      = 6,
  ACS_HANDLER_NONE      = 7
} Acs_error_handler_type;

typedef enum Acs_post_exception_mode {
  ACS_POST_EXCEPTION_ABORT   = 0,
  ACS_POST_EXCEPTION_DISCARD = 1,
  ACS_POST_EXCEPTION_PROCEED = 2,
  ACS_POST_EXCEPTION_QUERY   = 3,
  ACS_POST_EXCEPTION_DEFAULT = 4
} Acs_post_exception_mode;

/*
 * A parsed ACS_ERROR_HANDLER directive.
 * See: acslib.c:acs_lookup_error_handler()
 */
typedef struct Acs_error_handler {
  Acs_error_handler_type type;
  char *url_pattern;
  char *code_string;
  char *handler_action;
} Acs_error_handler;

typedef struct Acs_result {
  char *chaining;
  char *pass;
  char *pass_http_cookie;
  char *caching;
  Acl_url_match m;
  Credentials *cr;
  Grant_attrs attrs;
  Kwv *auth_kwv;
  char *redirect_action;
  Acs_denial_reason denial_reason;
  Dsvec *notices;
  char *errmsg;
} Acs_result;
  
typedef enum Acs_expr_result {
  /* The code for False must be zero. */
  ACS_EXPR_FALSE         = 0,

  /* The code for True must be positive and non-zero. */
  ACS_EXPR_TRUE          = 1,

  /* All error codes must be negative. */
  ACS_EXPR_SYNTAX_ERROR  = -2,
  ACS_EXPR_EVAL_ERROR    = -3,
  ACS_EXPR_LEXICAL_ERROR = -4
} Acs_expr_result;

static inline MAYBE_UNUSED int
acs_expr_error_occurred(Acs_expr_result st)
{

  return(st != ACS_EXPR_FALSE && st != ACS_EXPR_TRUE);
}

/* Lexical tokens */
typedef enum {
  T_EOI         =  0,
  T_LPAREN      =  1,
  T_OR          =  2,
  T_AND         =  3,
  T_LT          =  4,
  T_LE          =  5,
  T_EQ          =  6,
  T_NE          =  7,
  T_GE          =  8,
  T_GT          =  9,
  T_PLUS        = 10,
  T_MINUS       = 11,
  T_TIMES       = 12,
  T_DIV         = 13,
  T_MOD         = 14,
  T_EXP         = 15,
  T_DOT         = 16,
  T_COND        = 17,
  T_COLON       = 18,
  T_RPAREN      = 20,
  T_STRING      = 21,
  T_INTEGER     = 22,
  T_REAL        = 23,
  T_NOT         = 24,
  T_UNARY_PLUS  = 25,
  T_UNARY_MINUS = 26,
  T_FUNC        = 28,
  T_COMMA       = 29,
  T_LT_I        = 30,
  T_LE_I        = 31,
  T_EQ_I        = 32,
  T_NE_I        = 33,
  T_GT_I        = 34,
  T_GE_I        = 35,
  T_LITERAL     = 36,
  T_EOS         = 37,
  T_ASSIGN      = 38,
  T_BITOR       = 39,
  T_BITXOR      = 40,
  T_BITAND      = 41,
  T_BITSHL      = 42,
  T_BITSHR      = 43,
  T_BITCPL      = 44,
  T_PLUS_EQ     = 45,
  T_MINUS_EQ    = 46,
  T_TIMES_EQ    = 47,
  T_DIV_EQ      = 48,
  T_MOD_EQ      = 49,
  T_BITSHR_EQ   = 50,
  T_BITSHL_EQ   = 51,
  T_BITAND_EQ   = 52,
  T_BITXOR_EQ   = 53,
  T_BITOR_EQ    = 54,
  T_DOT_EQ      = 55,
  T_IF          = 56,
  T_ELSEIF      = 57,
  T_ELSE        = 58,
  T_LBRACE      = 59,
  T_RBRACE      = 60,
  T_PREINC      = 61,
  T_PREDEC      = 62,
  T_POSTINC     = 63,
  T_POSTDEC     = 64,
  T_BSTRING     = 65,
  T_CAST        = 66,
  T_BOOL        = 67,
  T_VARIABLE    = 68,
  T_LBRACKET    = 69,
  T_RBRACKET    = 70,
  T_LIST        = 71,
  T_LIST_REF    = 72,
  T_DOTDOT      = 73,
  T_HASH        = 74,
  T_ALIST       = 75,
  T_VOID        = 76,

  /* EXPERIMENTAL */
  T_B16U        = 77,
  T_B16         = 78,
  T_B10         = 79,
  T_B8          = 80,
  T_B2          = 81,

  T_UNDEF       = -1
} Token;

typedef struct Lex_state {
  Acs_environment *env;
  Dsvec *lexptr;
  char *expr;
  int ctn;
  int do_eval;		/* If 0, parse but do not evaluate */
  int exit_called;
  int block_end;
} Lex_state;
  
typedef struct Arglist {
  struct Expr_result *result;
  struct Arglist *next;
} Arglist;

/* A T_BSTRING */
typedef struct Bstring {
  void *data;
  size_t len;
} Bstring;

/* A T_LIST */
typedef struct List {
  Dsvec *list;		/* Vector of Values, indexed by non-neg. integer */
  Dsvec *dim;		/* Vector of integers defining the list's dimensions */
} List;

/* A T_ALIST */
typedef struct Alist {
  Kwv *kwv;			/* String-indexed values - an associative list */
} Alist;

/* XXX This means that lists cannot span negative values... */
#define DOTDOT_LAST		-1

/* A T_DOTDOT */
typedef struct Dotdot {
  long min;			/* A non-negative value */
  long max;			/* -1 selects the last element in a list */
} Dotdot;

typedef struct Value {
  /* T_INTEGER, T_STRING, T_BSTRING, T_UNDEF, etc. */
  Token token;
  int is_var_reference;
  int is_alist_reference;
  char *varname;
  int is_quoted;
  union {
	char *strval;
	long intval;
	double realval;
	Bstring bval;
	Dotdot dotdot;
	List listval;
	Alist alistval;
  } val;
} Value;

typedef struct Expr_result {
  Value value;
  Acs_expr_result err;
  char *errmsg;
  int exit_called;
} Expr_result;

typedef struct Acs_allow_result {
  Allow *allow;
  Expr_result *result;
} Acs_allow_result;

typedef struct Acs_deny_result {
  Deny *deny;
  Expr_result *result;
} Acs_deny_result;

/* An access token for authorization caching. */
typedef struct Acs_token {
  char *issued_by;
  char *url_path;
  char *identity;
  Credentials *credentials;
  Grant_attrs *attrs;
  unsigned long expires;
  long limit;
  char *unique;
  struct Acs_token *next;
} Acs_token;

static inline MAYBE_UNUSED void
init_grant_attrs(Grant_attrs *a)
{

  a->constraint = NULL;
  a->permit_chaining = NULL;
  a->pass_credentials = NULL;
  a->pass_http_cookie = NULL;
  a->permit_caching = NULL;
}

typedef enum {
  URI_PATTERN_HOST_DN          = 0,
  URI_PATTERN_HOST_DN_STAR     = 1,
  URI_PATTERN_HOST_DN_PLUS     = 2,
  URI_PATTERN_HOST_ANY         = 3,
  URI_PATTERN_HOST_IP          = 4,
  URI_PATTERN_HOST_INVALID     = -1,
} Uri_pattern_host_type;

typedef struct Uri_pattern {
  char *uri;				/* URI pattern that was parsed */
  int need_ssl;				/* 0==no SSL, 1==SSL, 2==either */
  char *scheme;				/* Canonicalized to lowercase */
  char *authority;
  char *userinfo;
  char *host;				/* Canonicalized to lowercase */
  Uri_pattern_host_type host_pat_type;
  struct in_addr host_addr;
  struct in_addr host_mask;
  in_port_t port;			/* Port number, if not 0 */
  char *port_given;			/* Port string as given in the URI, if any */
  char *path;				/* Decoded from the original URI */
  Dsvec *path_parts;		/* If non-NULL, components of original URI path */
} Uri_pattern;

#include "rlink.h"

#ifdef __cplusplus
extern "C" {
#endif

extern Acs_result *acs_init_result(Acs_result *result);
extern Acs_expr_result acs_eval(char *expr, Kwv *kwv, Kwv *args,
								Credentials *cr, char **result_str);
extern int is_undef(Value *value);
extern int acs_find_applicable_acl(char *item_type, Acs_environment *env,
								   Acl_file **best, int go,
								   Acs_result *result);
extern int acl_grants_user(Acl_rule *acl, Acs_environment *env,
						   Credentials **cp, Acs_result *res);
extern Acs_environment *acs_new_env(Acs_environment *env);
extern int acs_init_env(Kwv *kwv, Kwv *args, char *url, Credentials *c,
						Acs_environment *env);
extern Ds *acs_variable_resolve(Var *var, void *arg, void *vresp,
								int *undef);

extern Dsvec *get_acls(Vfs_handle *h);
extern int get_acl_file_list(Vfs_handle *h, Acl_file ***af);
extern Dsvec *sort_acls(Dsvec *acls);

extern int check_acl(char *buf, Parse_xml_error *);
extern int parse_xml_acl(char *str, Acl_rule **acl);
extern Acs_expr_result acs_expr(char *expr, Acs_environment *env,
								Expr_result *result);
extern char *acs_format_result(Expr_result *res);
extern Acs_expr_result acs_expr_string(char *expr, Acs_expr_ns_arg *ns_args,
									   char **result_str);
extern Acs_expr_result acs_expr_string_env(char *expr, Acs_environment *env,
										   char **result_str);
extern Acs_expr_result acs_expr_list(char *vfs_uri, char *item_type,
									 Acs_expr_ns_arg *ns_args,
									 char **result_str);
extern Acs_expr_result acs_expr_list_eval(Dsvec *dsv, Acs_expr_ns_arg *ns_args,
										  char **result_str);
extern Ds *acs_string_operand(char *str, Acs_environment *env);
extern Acs_expr_ns_arg *var_ns_to_acs_ns(Var_ns *namespaces);

extern int acs_match_url_segs(char *url_pattern, Dsvec *dsv_req, int *exact);

extern int acs_init_argv_namespace(Acs_environment *env, int argc,
								   char **argv);
extern int acs_is_readonly_namespace(char *ns);

extern int acs_token_create(Credentials *selected,
							Acs_result *result, char **cookie_buf);
extern int acs_token_grants(Credentials *selected, char *uri, Cookie *cookies,
							Acs_result *result);
extern int acs_token_cleanup(void);
extern Acs_error_handler *acs_lookup_error_handler(Acs_denial_reason reason,
												   char *request_uri);
extern Acs_denial_reason acs_lookup_error_code(char *code_str);
extern int acs_emit_access_denial(FILE *fp, Acs_denial_reason denial_reason,
								  char *request_uri, char *denial_msg,
								  Acs_result *result);
extern int acs_emit_access_granted(FILE *fp);
extern int acs_emit_access_error(FILE *fp, int code, char *mesg);
extern int acs_emit_set_header(char *fmt, ...);
extern int acs_emit_add_header(char *fmt, ...);
extern int acs_emit_header(char *name, char *fmt, ...);
extern int acs_emit_vheader(char *name, char *fmt, va_list ap);

extern char *acs_make_notice_presentation_args(Acs_result *result,
											   char **notice_uris,
											   char **resource_uris,
											   char **time_str, char **hmac);
extern Acs_error_handler *acs_init_error_handler(char *code, char *url_pattern,
												 char *action,
												 Acs_error_handler_type type);

extern int acs_add_success_expr(char *expr);
extern int acs_get_success_exprs(char ***list);
extern int acs_success(Kwv *kwv, Kwv *kwv_args, Acs_result *result);

extern char *acl_build_name(Dsvec *rparts, Acl_status status);
extern int acl_enable(Vfs_handle *h, char *path);
extern int acl_disable(Vfs_handle *h, char *path);

extern char *acl_xml_text(Acl_rule *acl);
extern char *acl_start_xml_text(Ds *ods, Acl_rule *acl);
extern char *acl_end_xml_text(Ds *ods, Acl_rule *acl);
extern char *acl_rules_xml_text(Ds *ds, Rule *rules);
extern char *acl_services_xml_text(Ds *ods, Services *services);
extern char *acl_identities_xml_text(Ds *ods, Identity *identities);

extern Ds *acs_elide_comments(char *expr);

extern Acl_index *read_acl_index(Vfs_handle *h);
extern Dsvec *list_indexed_acls(Vfs_handle *h);
extern Acl_index *get_acl_index(char *item_type);
extern Acl_rule *read_indexed_acl(char *item_type, Acl_map *am, char **xml);

extern Dsvec *access_token_list(void);
extern int access_token_delete(char *key);
extern Kwv *access_token_get(char *key);

extern int is_valid_id_attr(Dsvec *dsv, char *id);

extern Value *init_value(Value *v, Token token, ...);

extern Uri_pattern *uri_parse_pattern(char *str);
extern int uri_pattern_match(Uri *uri, Uri_pattern *pat);
extern int uri_pattern_match_selftest(void);

#ifdef __cplusplus
}
#endif

#endif
