/*
 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#ifndef _WEBDAV_CACHE_H_INCLUDE
#define _WEBDAV_CACHE_H_INCLUDE

#include <sys/types.h>
#include <sys/queue.h>

/*****************************************************************************/

/* define node_head structure */
LIST_HEAD(node_head, node_entry);

struct node_entry
{
	LIST_ENTRY(node_entry)  entries;				/* the other nodes on the parent's children list */
	struct node_entry		*parent;				/* the parent node_entry, or NULL if this is the root node */
	LIST_HEAD(, node_entry) children;				/* this node's children (if any) */
	
	/*
	 * Node identification fields
	 */
	size_t					name_length;			/* length of name */
	char					*name;					/* the utf8 name */
	ino_t					fileid;					/* file ID number */
	webdav_filetype_t		node_type;				/* (int) either WEBDAV_FILE_TYPE or WEBDAV_DIR_TYPE */
	u_int32_t				flags;
	
	/*
	 * Attribute fields
	 *
	 * Set attr_time to 0 and free the memory used by attr_ref (if any) to invalidate attributes.
	 */
	uid_t					attr_uid;				/* user authorized to use attribute data */
	time_t					attr_time;				/* local time - when attribute data was received from server */
	struct stat				attr_stat;				/* stat attributes */
	/* file system specific attribute data */
	char					*attr_appledoubleheader; /* NULL if no appledoubleheader data */
	
	/*
	 * File cache fields
	 *
	 * Clear the nodeInFileList flag bit and insert into the from the file_list make it a valid cache file.
	 *
	 * Clear the nodeInFileList flag bit and remove from the file_list to invalidate a cache file. Don't forget to free the 
	 * file_ref before you free the node.
	 *
	 * A valid cache file can be either active (the file or directory it is a
	 * cache for is open) or inactive (the file or directory it is a cache for
	 * is closed). Active cache files have a file_inactive_time of zero;
	 * inactive cache files have the time the cache file was made inactive so
	 * that they can be aged out of the cache.
	 */
	LIST_ENTRY(node_entry)  file_list;				/* the g_file_list */
	int						file_fd;				/* the cache file's file descriptor or -1 if none */
	u_int32_t				file_status;			/* the status of the cache file download:
													 *		WEBDAV_DOWNLOAD_NEVER (never downloaded)
													 *		WEBDAV_DOWNLOAD_IN_PROGRESS (download still in progress)
													 *		WEBDAV_DOWNLOAD_FINISHED (download complete)
													 *		WEBDAV_DOWNLOAD_ABORTED (download was stopped before complete because of close or error)
													 * If WEBDAV_DOWNLOAD_TERMINATED is set, an in-progress download should be stopped.
													 * Note: the download_status field should be word aligned.
													 */
	time_t					file_validated_time;	/* local time - when cache file was last validated by server */
	time_t					file_inactive_time;		/* local time - when cache file was made inactive (the file this cache is for was closed) - 0 if active */
	/* file system specific file cache data */
	time_t					file_last_modified;		/* the HTTP-date converted to time_t from the Last-Modified entity-header or from the getlastmodified property, or -1 if no valid Last-Modified date */
	char					*file_entity_tag;		/* The entity-tag from the ETag response-header or from the getetag property */
	uid_t					file_locktoken_uid;		/* the uid associated with the locktoken (filesystem_close and filesystem_lock need it to renew locks and to unlock). */
	char					*file_locktoken;		/* the lock token, or NULL */
};

#define WEBDAV_DOWNLOAD_NEVER		0
#define WEBDAV_DOWNLOAD_IN_PROGRESS	1
#define WEBDAV_DOWNLOAD_FINISHED	2
#define WEBDAV_DOWNLOAD_ABORTED		3
#define WEBDAV_DOWNLOAD_STATUS_MASK	0x7fffffff
#define WEBDAV_DOWNLOAD_TERMINATED	0x80000000

/* node_entry flags */
enum
{
	nodeDeletedBit		= 0,			/* the node is deleted and is on the deleted list */ 
	nodeDeletedMask		= 0x00000001,
	nodeInFileListBit	= 1,			/* the node is cached and is on the file list */
	nodeInFileListMask	= 0x00000002
};

/*****************************************************************************/

#define ATTRIBUTES_TIMEOUT_MIN		2		/* Minimum number of seconds attributes are valid */
#define ATTRIBUTES_TIMEOUT_MAX		60		/* Maximum number of seconds attributes are valid */
#define FILE_VALIDATION_TIMEOUT		60		/* Number of seconds file is valid from file_validated_time */
#define FILE_CACHE_TIMEOUT			3600	/* 1 hour */

#define NODE_IS_DELETED(node)		(((node)->flags & nodeDeletedMask) != 0)

int node_attributes_valid(struct node_entry *node, uid_t uid);
										  
#define NODE_FILE_IS_CACHED(node)	( ((node)->flags & nodeInFileListMask) != 0 )
#define NODE_FILE_IS_OPEN(node)		( (node)->file_inactive_time == 0 )
#define NODE_FILE_CACHE_INVALID(node) ( ((node)->file_inactive_time != 0) && \
									  (time(NULL) >= ((node)->file_inactive_time + FILE_CACHE_TIMEOUT)) )
#define NODE_FILE_INVALID(node)		( ((node)->file_validated_time == 0) || \
									  (time(NULL) >= ((node)->file_validated_time + FILE_VALIDATION_TIMEOUT)) )

/*****************************************************************************/

int nodecache_init(
	size_t name_length,				/* length of root node name */
	char *name,						/* the utf8 name of root node */
	struct node_entry **root_node);	/* the root node */

int nodecache_get_node(
	struct node_entry *parent,		/* the parent node_entry */
	size_t name_length,				/* length of name */
	const char *name,				/* the utf8 name of the node */
	int make_entry,					/* TRUE if a new node_entry should be created if needed*/
	webdav_filetype_t node_type,	/* if make_entry is TRUE, the type of node to create */
	struct node_entry **node);		/* the found (or new) node_entry */

void nodecache_free_nodes(void);

int nodecache_move_node(
	struct node_entry *node,		/* the node_entry to move */
	struct node_entry *new_parent,  /* the new parent node_entry */
	size_t new_name_length,			/* length of new_name or 0 if not renaming */
	char *new_name);				/* the utf8 new name of the node or NULL */

int nodecache_delete_node(
		struct node_entry *node);	/* the node_entry to delete (and possibly remove) */

int nodecache_add_attributes(
	struct node_entry *node,		/* the node_entry to update or add attributes_entry to */
	uid_t uid,						/* the uid these attributes are valid for */
	struct stat *statp,				/* the stat buffer */
	char *appledoubleheader);		/* pointer appledoubleheader or NULL */
	
int nodecache_remove_attributes(
	struct node_entry *node);		/* the node_entry to remove attributes from */

void nodecache_invalidate_caches(void);

int nodecache_add_file_cache(
	struct node_entry *node,		/* the node_entry to add a file_cache_entry to */
	int fd);						/* the file descriptor of the cache file */
	
void nodecache_remove_file_cache(
	struct node_entry *node);		/* the node_entry to remove file_cache_entry from */

struct node_entry *nodecache_get_next_file_cache_node(
	int get_first);					/* if true, return first file cache node; otherwise, the next one */

int nodecache_get_path_from_node(
	struct node_entry *target_node,	/* -> node */
	char **path);					/* <- relative path to root node */

/*****************************************************************************/

#ifdef DEBUG
void nodecache_display_node_tree(void);
void nodecache_display_file_cache(void);
#endif

/*****************************************************************************/

#endif
