/*
 * Copyright (c) 1999-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@
 */
/*
 * Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved
 */
/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * The NEXTSTEP Software License Agreement specifies the terms
 * and conditions for redistribution.
 *
 *	@(#)webdav.h	8.4 (Berkeley) 1/21/94
 */

#ifndef _WEBDAV_H_INCLUDE
#define _WEBDAV_H_INCLUDE

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/queue.h>
#include <sys/lock.h>
#include <sys/ioccom.h>
#include <mach/boolean.h>

/* Webdav file operation constants */
#define WEBDAV_LOOKUP			1
#define WEBDAV_CREATE			2
#define WEBDAV_OPEN				3
#define WEBDAV_CLOSE			4
#define WEBDAV_GETATTR			5
#define WEBDAV_SETATTR			6
#define WEBDAV_READ				7
#define WEBDAV_WRITE			8
#define WEBDAV_FSYNC			9
#define WEBDAV_REMOVE			10
#define WEBDAV_RENAME			11
#define WEBDAV_MKDIR			12
#define WEBDAV_RMDIR			13
#define WEBDAV_READDIR			14
#define WEBDAV_STATFS			15
#define WEBDAV_UNMOUNT			16
#define WEBDAV_INVALCACHES		17
/* for the future */
#define WEBDAV_LINK				18
#define WEBDAV_SYMLINK			19
#define WEBDAV_READLINK			20
#define WEBDAV_MKNOD			21
#define WEBDAV_GETATTRLIST		22
#define WEBDAV_SETATTRLIST		23
#define WEBDAV_EXCHANGE			24
#define WEBDAV_READDIRATTR		25
#define WEBDAV_SEARCHFS			26
#define WEBDAV_COPYFILE			27

/* Webdav file type constants */
#define WEBDAV_FILE_TYPE		1
#define WEBDAV_DIR_TYPE			2

/* Shared (kernel & processs) WebDAV structures */

typedef int webdav_filetype_t;

/* Shared (kernel & process) WebDAV defninitions */

/*
 * object_ref is the reference used to find a file system object in userland.
 * Lookup returns it. The rest of the operations which act upon a file system
 * object use it.
 */
typedef int object_ref;

/*
 * IMPORTANT: struct user_webdav_args, struct webdav_args, and webdav_mount()
 * must all be changed if the structure changes.
 */
struct user_webdav_args
{
	user_addr_t pa_mntfromname;					/* mntfromname */
	int	pa_version;								/* argument struct version */
	int pa_socket_namelen;						/* Socket to server name length */
	user_addr_t pa_socket_name;					/* Socket to server name */
	user_addr_t pa_vol_name;					/* volume name */
	u_int32_t pa_flags;							/* flag bits for mount */
	object_ref pa_root_obj_ref;					/* root object_ref */
	ino_t pa_root_fileid;						/* root fileid */
	off_t pa_dir_size;							/* size of directories */
	/* pathconf values: >=0 to return value; -1 if not supported */
	int pa_link_max;							/* maximum value of a file's link count */
	int pa_name_max;							/* The maximum number of bytes in a file name (does not include null at end) */
	int pa_path_max;							/* The maximum number of bytes in a relative pathname (does not include null at end) */
	int pa_pipe_buf;							/* The maximum number of bytes that can be written atomically to a pipe (usually PIPE_BUF if supported) */
	int pa_chown_restricted;					/* Return _POSIX_CHOWN_RESTRICTED if appropriate privileges are required for the chown(2) */
	int pa_no_trunc;							/* Return _POSIX_NO_TRUNC if file names longer than KERN_NAME_MAX are truncated */
	/* end of webdav_args version 1 */
};

struct webdav_args
{
	char *pa_mntfromname;						/* mntfromname */
	int	pa_version;								/* argument struct version */
	int pa_socket_namelen;						/* Socket to server name length */
	struct sockaddr *pa_socket_name;			/* Socket to server name */
	char *pa_vol_name;							/* volume name */
	u_int32_t pa_flags;							/* flag bits for mount */
	object_ref pa_root_obj_ref;					/* root object_ref */
	ino_t pa_root_fileid;						/* root fileid */
	off_t pa_dir_size;							/* size of directories */
	/* pathconf values: >=0 to return value; -1 if not supported */
	int pa_link_max;							/* maximum value of a file's link count */
	int pa_name_max;							/* The maximum number of bytes in a file name (does not include null at end) */
	int pa_path_max;							/* The maximum number of bytes in a relative pathname (does not include null at end) */
	int pa_pipe_buf;							/* The maximum number of bytes that can be written atomically to a pipe (usually PIPE_BUF if supported) */
	int pa_chown_restricted;					/* Return _POSIX_CHOWN_RESTRICTED if appropriate privileges are required for the chown(2) */
	int pa_no_trunc;							/* Return _POSIX_NO_TRUNC if file names longer than KERN_NAME_MAX are truncated */
	/* end of webdav_args version 1 */
};

/* Defines for webdav_args pa_flags field */
#define WEBDAV_SUPPRESSALLUI	0x00000001		/* SuppressAllUI flag */
#define WEBDAV_SECURECONNECTION	0x00000002		/* Secure connection flag (the connection to the server is secure) */

struct webdav_cred
{
	uid_t pcr_uid;								/* From ucred */
	short pcr_ngroups;							/* From ucred */
	gid_t pcr_groups[NGROUPS];					/* From ucred */
};

/* WEBDAV_LOOKUP */
struct webdav_request_lookup
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		dir;				/* directory to search */
	size_t			name_length;		/* length of name */
	char			name[];				/* filename to find */
};

struct webdav_reply_lookup
{
	object_ref		obj_ref;			/* object_ref of object corresponding to name */
	ino_t			obj_fileid;			/* object's file ID number */
	webdav_filetype_t obj_type;			/* WEBDAV_FILE_TYPE or WEBDAV_DIR_TYPE */
	struct timespec obj_atime;			/* time of last access */
	struct timespec obj_mtime;			/* time of last data modification */
	struct timespec obj_ctime;			/* time of last file status change */
	off_t			obj_filesize;		/* filesize of object */
};	

/* WEBDAV_CREATE */
struct webdav_request_create
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		dir;				/* The object_ref for the directory in which the file is to be created */
	mode_t			mode;				/* file type and initial file access permissions for the file */
	size_t			name_length;		/* length of name */
	char			name[];				/* The name that is to be associated with the created file */
};

struct webdav_reply_create
{
	object_ref		obj_ref;			/* object_ref of file corresponding to name */
	ino_t			obj_fileid;			/* file's file ID number */
};

/* WEBDAV_MKDIR */
struct webdav_request_mkdir
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		dir;				/* The object_ref for the directory in which the file is to be created */
	mode_t			mode;				/* file type and initial file access permissions for the file */
	size_t			name_length;		/* length of name */
	char			name[];				/* The name that is to be associated with the created directory */
};

struct webdav_reply_mkdir
{
	object_ref		obj_ref;			/* object_ref of directory corresponding to name */
	ino_t			obj_fileid;			/* directory's file ID number */
};

/* WEBDAV_OPEN */
struct webdav_request_open
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		obj_ref;			/* object_ref of object */
	int				flags;				/* file access flags (O_RDONLY, O_WRONLY, etc.) */
	int				ref;				/* the reference to the webdav object that the cache object should be associated with */
};

struct webdav_reply_open
{
	pid_t			pid;				/* process ID of file system daemon (for matching to ref's pid) */
};

/* WEBDAV_CLOSE */
struct webdav_request_close
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		obj_ref;			/* object_ref of object */
};

struct webdav_reply_close
{
};

/* WEBDAV_GETATTR */
struct webdav_request_getattr
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		obj_ref;			/* object_ref of object */
};

struct webdav_reply_getattr
{
	struct stat		obj_attr;			/* attributes for the object */
};

/* WEBDAV_SETATTR XXX not needed at this time */
struct webdav_request_setattr
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		obj_ref;			/* object_ref of object */
	struct stat		new_obj_attr;		/* new attributes of the object */
};

struct webdav_reply_setattr
{
};

/* WEBDAV_READ */
struct webdav_request_read
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		obj_ref;			/* object_ref of file object */
	off_t			offset;				/* position within the file object at which the read is to begin */
	size_t			count;				/* number of bytes of data to be read (limited to WEBDAV_MAX_IO_BUFFER_SIZE (8000-bytes)) */
};

struct webdav_reply_read
{
};

/* WEBDAV_WRITE XXX not needed at this time */
struct webdav_request_write
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		obj_ref;			/* object_ref of file object */
	off_t			offset;				/* position within the file object at which the write is to begin */
	size_t			count;				/* number of bytes of data to be written (limited to WEBDAV_MAX_IO_BUFFER_SIZE (8000-bytes)) */
	char			data[];				/* data to be written to the file object */
};

struct webdav_reply_write
{
	size_t			count;				/* number of bytes of data written to the file */
};

/* WEBDAV_FSYNC */
struct webdav_request_fsync
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		obj_ref;			/* object_ref of object */
};

struct webdav_reply_fsync
{
};

/* WEBDAV_REMOVE */
struct webdav_request_remove
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		obj_ref;			/* object_ref of entry to remove */
};

struct webdav_reply_remove
{
};

/* WEBDAV_RMDIR */
struct webdav_request_rmdir
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		obj_ref;			/* object_ref of directory object to remove */
};

struct webdav_reply_rmdir
{
};

/* WEBDAV_RENAME */
struct webdav_request_rename
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		from_dir_ref;		/* object_ref for the directory from which the entry is to be renamed */
	object_ref		from_obj_ref;		/* object_ref for the object to be renamed */
	object_ref		to_dir_ref;			/* object_ref for the directory to which the object is to be renamed */
	object_ref		to_obj_ref;			/* object_ref for the object's new location if it exists (may be NULL) */
	size_t			to_name_length;		/* length of to_name */
	char			to_name[];			/* new name for the object */
};

struct webdav_reply_rename
{
};

/* WEBDAV_READDIR */
struct webdav_request_readdir
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref		obj_ref;			/* object_ref of directory to read */
	int				cache;				/* if TRUE, perform additional caching */
};

struct webdav_reply_readdir
{
};

/* WEBDAV_STATFS */
struct webdav_request_statfs
{
	struct webdav_cred pcr;				/* user and groups */
	object_ref	root_obj_ref;			/* object_ref of the root directory */
};

struct webdav_reply_statfs
{
	struct statfs   fs_attr;			/* file system information */
										/*
										 * (required: f_bsize, f_iosize, f_blocks, f_bfree,
										 * f_bavail, f_files, f_ffree. The kext will either copy
										 * the remaining info from the mount struct, or the cached
										 * statfs struct in the mount struct IS the destination.
										 */
};

/* WEBDAV_UNMOUNT */
struct webdav_request_unmount
{
	struct webdav_cred pcr;				/* user and groups */
};

struct webdav_reply_unmount
{
};

/* WEBDAV_INVALCACHES */
struct webdav_request_invalcaches
{
	struct webdav_cred pcr;				/* user and groups */
};

struct webdav_reply_invalcaches
{
};

union webdav_request
{
	struct webdav_request_lookup	lookup;
	struct webdav_request_create	create;
	struct webdav_request_open		open;
	struct webdav_request_close		close;
	struct webdav_request_getattr   getattr;
	struct webdav_request_setattr	setattr;
	struct webdav_request_read		read;
	struct webdav_request_write		write;
	struct webdav_request_fsync		fsync;
	struct webdav_request_remove	remove;
	struct webdav_request_rmdir		rmdir;
	struct webdav_request_rename	rename;
	struct webdav_request_readdir	readdir;
	struct webdav_request_statfs	statfs;
	struct webdav_request_invalcaches invalcaches;
};

union webdav_reply
{
	struct webdav_reply_lookup		lookup;
	struct webdav_reply_create		create;
	struct webdav_reply_open		open;
	struct webdav_reply_close		close;
	struct webdav_reply_getattr		getattr;
	struct webdav_reply_setattr		setattr;
	struct webdav_reply_read		read;
	struct webdav_reply_write		write;
	struct webdav_reply_fsync		fsync;
	struct webdav_reply_remove		remove;
	struct webdav_reply_rmdir		rmdir;
	struct webdav_reply_rename		rename;
	struct webdav_reply_readdir		readdir;
	struct webdav_reply_statfs		statfs;
	struct webdav_reply_invalcaches	invalcaches;
};

#define UNKNOWNUID ((uid_t)99)

/*
 * The WEBDAV_CONNECTION_DOWN_MASK bit is set by the code in send_reply() in
 * activate.c in the int result when the mount_webdav daemon determines it cannot
 * communicate with the remote WebDAV server. webdav_sendmsg() and webdav_open() in
 * webdav_vnops.c check that bit to determine if the connection is up or down.
 */
#define WEBDAV_CONNECTION_DOWN_MASK	0x80000000

/*
 * WEBDAVIOC_INVALIDATECACHES commmand passed to fsctl(2) causes WebDAV FS to
 * revalidate cached files with the WebDAV server and to invalidate all
 * all cached stat data.
 * example:
 * result = fsctl(path, WEBDAVIOC_INVALIDATECACHES, NULL, 0);
 */
#define	WEBDAVIOC_INVALIDATECACHES	_IO('w', 1)
#define	WEBDAV_INVALIDATECACHES		IOCBASECMD(WEBDAVIOC_INVALIDATECACHES)

/*
 * Sysctl values for WebDAV FS
 */
 
/*
 * If name[0] is WEBDAV_ASSOCIATECACHEFILE_SYSCTL, then 
 *		name[1] = a pointer to a struct open_associatecachefile
 *		name[2] = fd of cache file
 */
#define WEBDAV_ASSOCIATECACHEFILE_SYSCTL   1

#define WEBDAV_MAX_KEXT_CONNECTIONS 128			/* maximum number of open connections to user-land server */

#ifdef KERNEL

struct webdavmount
{
	vnode_t pm_root;							/* Root node */
	u_int32_t pm_status;						/* status bits for this mounted structure */
	struct mount *pm_mountp;					/* vfs structure for this filesystem */
	char *pm_vol_name;							/* volume name */
	struct sockaddr *pm_socket_name;			/* Socket to server name */
	u_int32_t pm_open_connections;				/* number of connections opened to user-land server */
	off_t pm_dir_size;							/* size of directories */
	/* pathconf values: >=0 to return value; -1 if not supported */
	int pm_link_max;							/* maximum value of a file's link count (1 for file systems that do not support link counts) */
	int pm_name_max;							/* The maximum number of bytes in a file name (does not include null at end) */
	int pm_path_max;							/* The maximum number of bytes in a relative pathname (does not include null at end) */
	int pm_pipe_buf;							/* The maximum number of bytes that can be written atomically to a pipe (usually PIPE_BUF if supported) */
	int pm_chown_restricted;					/* Return _POSIX_CHOWN_RESTRICTED if appropriate privileges are required for the chown(2); otherwise 0 */
	int pm_no_trunc;							/* Return _POSIX_NO_TRUNC if file names longer than KERN_NAME_MAX are truncated; otherwise 0 */
};

struct webdavnode
{
	LIST_ENTRY(webdavnode) pt_hash;				/* Hash chain. */
	struct mount *pt_mountp;					/* vfs structure for this filesystem */
	vnode_t pt_parent;							/* Pointer to parent vnode */
	vnode_t pt_vnode;							/* Pointer to vnode */
	vnode_t pt_cache_vnode;						/* Pointer to cached file vnode */
	object_ref pt_obj_ref;						/* object_ref from lookup */
	ino_t pt_fileid;							/* file id */
	struct timespec pt_atime;					/* time of last access */
	struct timespec pt_mtime;					/* time of last data modification */
	struct timespec pt_ctime;					/* time of last file status change */
	off_t pt_filesize;							/* what we think the filesize is */
	u_int32_t pt_status;						/* WEBDAV_DIRTY, etc */
	u_int32_t pt_opencount;						/* reference count of opens */
};

struct open_associatecachefile
{
	vnode_t cachevp;
	pid_t   pid;
};

/* Defines for webdavnode pt_status field */

#define WEBDAV_DIRTY			0x00000001		/* Indicates webdavnode has data which has not been flushed to cache file */
#define WEBDAV_ACCESSED			0x00000002		/* Indicates file has been accessed - used by webdav_gettr to determine dates */
#define WEBDAV_ONHASHLIST		0x00000004		/* Indicates webdavnode is on the hash chain */
#define WEBDAV_DELETED			0x00000008		/* Indicates that webdav file (which is still referenced) has been deleted */
#define WEBDAV_DIR_NOT_LOADED   0x00000010		/* Indicates that an open directory is empty and needs to be populated from the server */
#define WEBDAV_INIT				0x00000020		/* Indicates that the webdavnode is in the process of being initialized */
#define WEBDAV_WAITINIT			0x00000040		/* Indicates that someone is sleeping (on webdavnode) waiting for initialization to finish */
#define WEBDAV_ISMAPPED			0x00000080		/* Indicates that the file is mapped */
#define WEBDAV_WASMAPPED		0x00000100		/* Indicates that the file is or was mapped */

/* Defines for webdavmount pm_status field */

#define WEBDAV_MOUNT_SUPPORTS_STATFS 0x00000001	/* Indicates that the server supports quata and quota used properties */
#define WEBDAV_MOUNT_STATFS 0x00000002			/* statfs is in progress */
#define WEBDAV_MOUNT_STATFS_WANTED 0x00000004	/* statfs wakeup is wanted */
#define WEBDAV_MOUNT_TIMEO 0x00000008			/* connection to webdav server was lost */
#define WEBDAV_MOUNT_DEAD 0x00000010			/* file system is dead. */
#define WEBDAV_MOUNT_SUPPRESS_ALL_UI 0x00000020	/* suppress UI when connection is lost */
#define WEBDAV_MOUNT_CONNECTION_WANTED 0x000000040 /* wakeup is wanted to start another connection with user-land server */
#define WEBDAV_MOUNT_SECURECONNECTION 0x000000080 /* the connection to the server is secure */

/* Webdav sizes for statfs */

#define WEBDAV_NUM_BLOCKS -1					/* not supported */
#define WEBDAV_FREE_BLOCKS	-1					/* not supported */
#define WEBDAV_NUM_FILES 65535					/* Like HFS */
#define WEBDAV_FREE_FILES (WEBDAV_NUM_FILES - 2) /* Used a couple */

/* Webdav status macros */

#define VFSTOWEBDAV(mp) ((struct webdavmount *)(vfs_fsprivate(mp)))
#define VTOWEBDAV(vp) ((struct webdavnode *)(vnode_fsnode(vp)))
#define WEBDAVTOV(pt) ((pt)->pt_vnode)

/* Other defines */

/* The WEBDAV_MAX_IO_BUFFER_SIZE gates how many bytes we
 * will try to read with a byte range request to the server.
 * Because sockets are only so big we can't transfer 8192 bytes.
 * That many bytes won't fit in a buffer so rather than having
 * webdav_sendmsg wait for all data, it would have to loop.	 Limiting
 * at 8000 works (based on emprical study on Darwin). If you are porting
 * this code to another platform or if the default socket buffer size
 * changes you may need to change this constan to implement the looping.
 * None of this would be necessary if Darwin's soreserve actually did reserve
 * space rather than just enforcing limits
 */
 
#define WEBDAV_MAX_IO_BUFFER_SIZE 8000	/* Gates byte read optimization */

/*
 * In webdav_read and webdav_pagein, webdav_read_bytes is called if the part of
 * file we need hasn't been downloaded from the server yet. However, since we're
 * already downloading the file, there's already data in the stream so reading
 * a range is counterproductive if we'll have downloaded the part we need (in the
 * stream) by the time webdav_read_bytes returns the data out of band.
 * Apache's mod_dav buffers 32K in the stream, so that's we'll use.
 */ 
#define WEBDAV_WAIT_IF_WITHIN	32768

/*
 * There are several loops where the code waits for a cache file to be downloaded,
 * or for a specific part of the cache file to be downloaded. This constant controls
 * how often the cache vnode is polled (with VNOP_GETATTR). The less often we poll,
 * the higher the latency between getting data and using it.
 *
 * A network connection with 35mbps (maximum cable) can give us a page every millisecond.
 * A network connection with 3mbps (typical capped cable or high speed DSL) can give us a page every 10 milliseconds.
 * For typical home networks over cable or DSL, 10 ms should be OK. Thus... (10 * 1000 * 1000) nanoseconds.
 */
#define WEBDAV_WAIT_FOR_PAGE_TIME (10 * 1000 * 1000)

/* the number of seconds soreceive() should block
 * before rechecking the server process state
 */
#define WEBDAV_SO_RCVTIMEO_SECONDS 10

#ifdef DEBUG
	#define START_MARKER(str) \
	{ \
		log_vnop_start(str); \
	}
	#define RET_ERR(str, error) \
	{ \
		/*if (error)*/ \
			log_vnop_error(str, error); \
		return(error); \
	}
extern void log_vnop_start(char *str);
extern void log_vnop_error(char *str, int error);
#else
	#define START_MARKER(str)
	#define RET_ERR(str, error) return(error)
#endif


extern int( **webdav_vnodeop_p)();
extern void webdav_hashinit(void);
extern void webdav_hashdestroy(void);
extern void webdav_hashrem(struct webdavnode *);
extern void webdav_hashins(struct webdavnode *);
extern vnode_t webdav_hashget(struct mount *mp, ino_t fileid);

extern void webdav_copy_creds(vfs_context_t context, struct webdav_cred *dest);
extern int webdav_sendmsg(int vnop, struct webdavmount *fmp,
	void *request, size_t requestsize,
	void *vardata, size_t vardatasize,
	int *result, void *reply, size_t replysize);
extern int webdav_get(
	struct mount *mp,			/* mount point */
	vnode_t dvp,				/* parent vnode */
	int markroot,				/* if 1, mark as root vnode */
	struct componentname *cnp,  /* componentname */
	object_ref obj_ref,			/* object's object_ref */
	ino_t obj_fileid,			/* object's file ID number */
	enum vtype obj_vtype,		/* VREG or VDIR */
	struct timespec obj_atime,  /* time of last access */
	struct timespec obj_mtime,  /* time of last data modification */
	struct timespec obj_ctime,  /* time of last file status change */
	off_t obj_filesize,			/* object's filesize */
	vnode_t *vpp);				/* vnode returned here */

extern int webdav_assign_ref(struct open_associatecachefile *associatecachefile, int *ref);
extern void webdav_release_ref(int ref);
extern char webdav_name[MFSNAMELEN];
				  
#endif /* KERNEL */

#endif /*ifndef _WEBDAV_H_INCLUDE */

