#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "newhash.h"
#include "llist.h"
#include "lib.h"


/* ensure we only include unistd.h when we can */
#ifndef WIN32
#ifndef NOUNISTD
#include <unistd.h>
#endif
#endif


/* handle Visual C++ specific oddities */
#ifdef WIN32
#undef snprintf
#define snprintf _snprintf
#endif


#define BIN_SIZE		1000		/* if bin filenames exceed this size, they will be truncated */


/* enums returned by various functions */
enum {	BIN_OKAY,				/* bin file function performed correctly */
		BIN_FAIL,				/* miscellaneous nasal demons */
		BIN_FAIL_EOF,			/* failed to get next message as at end of bin file */
		BIN_FAIL_INIT,			/* bin file failed to initialise */
		BIN_FAIL_FILE_IO,		/* bin file failed to open due to IO error */
		BIN_FAIL_MALLOC,		/* bin file failed to open due to a failed malloc() call */
		BIN_STATUS_EOM,			/* at the end of a message in bin file */
		BIN_STATUS_EOF,			/* at the end of bin file */
		BIN_STATUS_ERROR,		/* can't read from bin file for some reason */
		BIN_STATUS_IN_MSG,		/* currently within a message in bin file */
		BIN_STATUS_CLOSED,		/* bin file has been closed by bin_close() */

		/* do not add any enums below BIN_NUM */		
		BIN_NUM
};


/*	Structure containing information about a set of bins.
 *
 *	This structure should NEVER be accessed directly - it should only be accessed
 *	by the functions that comprise the bins API.  Direct access to the contents
 *	of a bin structure is likely to invoke undefined behaviour.
 */
typedef struct _BIN_BINDATA {
	llist				message_list;			/* linked list of messages */
	char				bin_path[BIN_SIZE];		/* path containing bin files */
	int					status;					/* status of bin file */
	FILE				*bin_file;				/* current bin filename */
	int					bytes_read;				/* bytes read for current message */
	int					first;					/* 1 if at first message, 0 otherwise */
} bin_data;


/*	bin_open() opens bins for user 'user', domain 'domain', in path 
 *	'path'.  If 'bin_name' is not NULL, then 'bin_name' is used
 *	rather than creating bin filenamesfrom the aforementioned components.
 * 
 *	If non-empty, 'domain_prefix' is prepended to the username 'user'.
 *	bin_open() hashes the specified path using 'user' as the username and 
 *	'hash_spool' as the hashing level.  When hashing, all characters in the
 *	string pointed to by 'invalid' are considered to be invalid.
 *
 *	bin_open() places all bin info in the bin structure pointed to by 'bin'.
 *
 *	bin_open() returns one of the following conditions:
 *
 *		BIN_OKAY				bins were successfully opened
 *		BIN_FAIL				miscellaneous failure
 *		BIN_FAIL_INIT			failed to initialise bin structure
 *		BIN_FAIL_FILE_IO		specified maildrop can't be opened
 *		BIN_FAIL_MALLOC			failed to allocate memory - this is *bad*
 *		
 *	If bin_open() succeeds, the bin structure pointed to by 'bin' will
 *	be initialised, and bin_open() will return BIN_OKAY.  Once initialised,
 *	the bin structure may not be initialised again until it has been
 *	closed with bin_close().
 *
 *	If bin_open() fails, bin_open() will return BIN_FAIL_FILE_IO.
 *
 *	If bin_open() returns BIN_FAIL_FILE_IO, and 'failure' is not NULL, 
 *	*'failure' will be set to errno, thus allowing the calling function to 
 *	determine the reason for failure in detail.
 *
 *	If 'bin', 'user', 'path', 'domain_prefix' or 'invalid' are NULL, 
 *	bin_open() will fail, returning BIN_FAIL_INIT.
 */
int	bin_open(bin_data *bin, char *user, char *path, const char *domain_prefix, char *invalid, int hash_spool, int *failure, char *bin_name, char *reason);


/*	bin_dele() deletes the bins for user 'user', domain 'domain', in path 
 *	'path'.  If 'bin_name' is not NULL, then 'bin_name' is used
 *	rather than creating bin filenames from the aforementioned components. 
 * 
 *	If bin_dele() fails, bin_dele() will return BIN_FAIL_FILE_IO.
 *
 *	If bin_dele() returns BIN_FAIL_FILE_IO, and 'failure' is not NULL, 
 *	*'failure' will be set to errno, thus allowing the calling function to 
 *	determine the reason for failure in detail.
 *
 *	If 'bin', 'user', 'path', 'domain_prefix' or 'invalid' are NULL, 
 *	bin_dele() will fail, returning BIN_FAIL.
 */
int	bin_dele(char *user, char *path, const char *domain_prefix, char *invalid, int hash_spool, int *failure, char *bin_name);


/*	bin_next_message() moves to the next message in the bins pointed to by 
 *	'bin'. 
 *	
 *	If bin_next_message() succeeds, bin_next_message() returns BIN_OKAY if
 *	there is another message, or BIN_FAIL_EOF if at the end of the last message.
 *
 *	If bin_next_message() fails, bin_next_message() will return BIN_FAIL_FILE_IO.
 *
 *	If bin_next_message() returns BIN_FAIL_FILE_IO, and 'failure' is not NULL, 
 *	*'failure' will be set to errno, thus allowing the calling function to 
 *	determine the reason for failure in detail.
 *
 *	bin_next_message() returns BIN_FAIL if 'bin' is NULL.
 */
int bin_next_message(bin_data *bin, int *failure);


/*	bin_gets() attempts to read up to one character less than 'size' characters
 *	from bins 'bin'. 
 *	
 *	Reading stops at the end of the current message, or a newline.  Newlines are
 *	stored in the buffer; they are not discarded.
 *
 *	If bin_gets() succeeds, it returns 'buf', and returns NULL on failure or
 *	upon being passed a NULL 'bin'.
 */
char *bin_gets(char *buf, int size, bin_data *bin);


/*	bin_close() closes bin structure 'bin'.
 *
 *	If bin_close() succeeds, the bin structure pointed to by 'bin' will
 *	be closed, and bin_close() will return BIN_OKAY.  Any open files will be
 *	closed, and all allocated memory will be freed.  The bin structure 
 *	pointed to by 'bin' will then be free for use by bin_close().
 *
 *	If bin_close() fails, or if 'bin' is NULL, bin_close() will return 
 *	BIN_FAIL.
 */
int bin_close(bin_data *bin);


/*	bin_get_path() returns a pointer to a string containing the path
 *	of the bins contained within 'bin'.
 *
 *	Note that the path is only set after a call to bin_open() - calling
 *	bin_get_path() on a bin structure which has not been opened, or
 *	had an attempt at opening fail, will invoke undefined behaviour.
 *
 *	bin_get_path() will return NULL if 'bin' is NULL.
 */
const char *bin_get_path(bin_data *bin);



