
/*
 *  files.c
 *
 *  Copyright (C) 2006 Alex deVries
 *
 */

#include <stdlib.h>
#include <string.h>
#include "afpfs-ng/dsi.h"
#include "afpfs-ng/afp.h"
#include "afpfs-ng/utils.h"
#include "dsi_protocol.h"
#include "afpfs-ng/afp_protocol.h"
#include "afp_internal.h"
#include "afp_replies.h"

/* afp_setfileparms, afp_setdirparms and afpsetfiledirparms are all remarkably
   similiar.  We abstract them to afp-setparms_lowlevel. */

static int afp_setparms_lowlevel(struct afp_volume * volume,
	unsigned int dirid, const char * pathname, unsigned short bitmap,
	struct afp_file_info *fp, char command)
{
	struct {
		struct dsi_header dsi_header __attribute__((__packed__));
		uint8_t command;
		uint8_t pad;
		uint16_t volid;
		uint32_t dirid;
		uint16_t bitmap;
	}  __attribute__((__packed__)) *request_packet;
	struct afp_server * server=volume->server;
	unsigned short result_bitmap=0;
	unsigned int len = sizeof(*request_packet) + 
		sizeof_path_header(server)+strlen(pathname) + 
		200 +  /* This is the max size of a data block */
		1; /* In case we're not on an even boundary */

	char * msg, * pathptr, *p;
	int ret;

	if ((msg=malloc(len))==NULL) 
		return -1;
	pathptr= msg+sizeof(*request_packet);
	p = pathptr + sizeof_path_header(server)+strlen(pathname);

	if (((uintptr_t) p) & 0x1) p++;	/* Make sure we're on an even boundary */
	memset(msg,0,len);
	request_packet = (void *) msg;
	dsi_setup_header(server,&request_packet->dsi_header,DSI_DSICommand);
	request_packet->command=command;
	request_packet->pad=0;
	request_packet->volid=htons(volume->volid);
	request_packet->dirid=htonl(dirid);
	request_packet->bitmap=htons(bitmap);
	copy_path(server,pathptr,pathname,strlen(pathname));
	unixpath_to_afppath(server,pathptr);

	if (bitmap & kFPAttributeBit) {
		/* Todo: 
		The spec says: "The following parameters may be set or cleared:
		Attributes (all attributes except DAlreadyOpen,
		RAlreadyOpen, and CopyProtect)".  This should be checked.
		*/

		*p=htons(fp->attributes);
		result_bitmap|=kFPAttributeBit;
		p+=2;
	}

	if (bitmap & kFPCreateDateBit) {
		unsigned int * date = (void *) p;
		*date = AD_DATE_FROM_UNIX(fp->creation_date);
		result_bitmap|=kFPCreateDateBit;
		p+=4;
	}
	if (bitmap & kFPModDateBit) {
		unsigned int * date = (void *) p;
		*date = AD_DATE_FROM_UNIX(fp->modification_date);
		result_bitmap|=kFPModDateBit;
		p+=4;

	}
	if (bitmap & kFPBackupDateBit) {
		unsigned int * date = (void *) p;
		*date = AD_DATE_FROM_UNIX(fp->backup_date);
		p+=4;
		result_bitmap|=kFPBackupDateBit;
	}
	if (bitmap & kFPFinderInfoBit) {
		result_bitmap|=kFPFinderInfoBit;
		memcpy(p,fp->finderinfo,32);
		p+=32;;
	}
	if (bitmap & kFPUnixPrivsBit) {
		struct afp_unixprivs * t_unixprivs = (void *) p;
		memcpy(t_unixprivs,&fp->unixprivs,
			sizeof(struct afp_unixprivs));
		/* Convert the different components */
		t_unixprivs->uid=htonl(t_unixprivs->uid);
		t_unixprivs->gid=htonl(t_unixprivs->gid);
		t_unixprivs->permissions=htonl(t_unixprivs->permissions);
		t_unixprivs->ua_permissions=htonl(t_unixprivs->ua_permissions);
		
		p+=sizeof(struct afp_unixprivs);
		result_bitmap|=kFPUnixPrivsBit;
		

	}

	ret=dsi_send(server, (char *) msg,p-msg,DSI_DEFAULT_TIMEOUT, 
		command,NULL);

	free(msg);

	return ret;
}

int afp_setfileparms(struct afp_volume * volume,
	unsigned int dirid, const char * pathname, unsigned short bitmap,
	struct afp_file_info *fp)
{
	return afp_setparms_lowlevel(volume,dirid,pathname,bitmap,
		fp,afpSetFileParms);
}

int afp_setfiledirparms(struct afp_volume * volume,
	unsigned int dirid, const char * pathname, unsigned short bitmap,
	struct afp_file_info *fp)
{
	return afp_setparms_lowlevel(volume,dirid,pathname,bitmap,
		fp,afpSetFileDirParms);
}

int afp_setdirparms(struct afp_volume * volume,
	unsigned int dirid, const char * pathname, unsigned short bitmap,
	struct afp_file_info *fp)
{
	return afp_setparms_lowlevel(volume,dirid,pathname,bitmap,
		fp,afpSetDirParms);
}

int afp_delete(struct afp_volume * volume,
	unsigned int dirid, char * pathname)
{
	struct {
		struct dsi_header dsi_header __attribute__((__packed__));
		uint8_t command;
		uint8_t pad;
		uint16_t volid;
		uint32_t dirid;
	}  __attribute__((__packed__)) *request_packet;
	struct afp_server * server = volume->server;
	unsigned int len = sizeof(*request_packet)+sizeof_path_header(server)+strlen(pathname);
	char * pathptr, *msg;
	int ret=0;
	if ((msg=malloc(len))==NULL) {
		log_for_client(NULL,AFPFSD,LOG_ERR,
			"Out of memory\n");
		return -1;
	};
	pathptr = msg + (sizeof(*request_packet));
	request_packet=(void *) msg;

	dsi_setup_header(server,&request_packet->dsi_header,DSI_DSICommand);
	request_packet->command=afpDelete;
	request_packet->pad=0;
	request_packet->volid=htons(volume->volid);
	request_packet->dirid=htonl(dirid);
	copy_path(server,pathptr,pathname,strlen(pathname));
	unixpath_to_afppath(server,pathptr);

	ret=dsi_send(server, (char *) request_packet,len,DSI_DEFAULT_TIMEOUT, 
		afpDelete ,NULL);

	free(msg);
	
	return ret;
}


int afp_read(struct afp_volume * volume, unsigned short forkid, 
		uint32_t offset, 
		uint32_t count,
		struct afp_rx_buffer * rx)
{
	int rc;
	struct {
		struct dsi_header dsi_header __attribute__((__packed__));
		uint8_t command;
		uint8_t pad;
		uint16_t forkrefnum;
		uint32_t offset;
		uint32_t reqcount;
		uint8_t newlinemask;
		uint8_t newlinechar;
	}  __attribute__((__packed__)) readext_packet;

	dsi_setup_header(volume->server,&readext_packet.dsi_header,DSI_DSICommand);
	readext_packet.command=afpRead;
	readext_packet.pad=0x0;
	readext_packet.forkrefnum=htons(forkid);
	readext_packet.offset=htonl(offset);
	readext_packet.reqcount=htonl(count);
	readext_packet.newlinemask=0;
	readext_packet.newlinechar=0;
	rc=dsi_send(volume->server, (char *) &readext_packet,
		sizeof(readext_packet), DSI_DEFAULT_TIMEOUT, 
		afpRead, (void *) rx);
	return rc;
}

int afp_read_reply(struct afp_server *server, char * buf, unsigned int size,void * other )
{
	struct afp_rx_buffer * rx = other;
	struct dsi_header * header = (void *) buf;
	char * ptr = buf + sizeof(struct dsi_header);
	unsigned int rx_quantum = server->rx_quantum;

	size-=sizeof(struct dsi_header);

	if (size>rx_quantum) {
		log_for_client(NULL,AFPFSD,LOG_ERR,
			"This is definitely weird, I guess I'll just drop %d bytes\n",size-rx_quantum);
		size=rx_quantum;
	}
	memcpy(rx->data,ptr,size);
	rx->size=size;
	rx->errorcode=ntohl(header->return_code.error_code);
	return 0;
}

int afp_readext(struct afp_volume * volume, unsigned short forkid, 
		uint64_t offset, 
		uint64_t count,
		struct afp_rx_buffer * rx)
{
	int rc;
	struct {
		struct dsi_header dsi_header __attribute__((__packed__));
		uint8_t command;
		uint8_t pad;
		uint16_t forkrefnum;
		uint64_t offset;
		uint64_t reqcount;
	}  __attribute__((__packed__)) readext_packet;

	dsi_setup_header(volume->server,&readext_packet.dsi_header,DSI_DSICommand);
	readext_packet.command=afpReadExt;
	readext_packet.pad=0x0;
	readext_packet.forkrefnum=htons(forkid);
	readext_packet.offset=hton64(offset);
	readext_packet.reqcount=hton64(count);
	rc=dsi_send(volume->server, (char *) &readext_packet,
		sizeof(readext_packet), DSI_DEFAULT_TIMEOUT, 
		afpReadExt, (void *) rx);
	return rc;
}

int afp_readext_reply(struct afp_server *server, char * buf, unsigned int size, void * other)
{
	struct afp_rx_buffer * rx = other;
	struct dsi_header * header = (void *) buf;
	char * ptr = buf + sizeof(struct dsi_header);
	unsigned int rx_quantum = server->rx_quantum;

	size-=sizeof(struct dsi_header);

	if (size>rx_quantum) {
		log_for_client(NULL,AFPFSD,LOG_ERR,
			"This is definitely weird, I guess I'll just drop %d bytes\n",size-rx_quantum);
		size=rx_quantum;
	}
	memcpy(rx->data,ptr,size);
	rx->size=size;
	rx->errorcode=ntohl(header->return_code.error_code);
	return 0;
}

int afp_getfiledirparms_reply(struct afp_server *server, char * buf, unsigned int size,
	void * other)
{
	struct {
		struct dsi_header header __attribute__((__packed__));
		uint16_t filebitmap;
		uint16_t dirbitmap;
		uint8_t isdir;
		uint8_t pad;
	}  __attribute__((__packed__)) * reply_packet = (void *) buf;
	struct afp_file_info * filecur = other;

	if  (reply_packet->header.return_code.error_code) 
		return reply_packet->header.return_code.error_code;

	if (size<sizeof(*reply_packet)) 
		return -1;

	parse_reply_block(server, 
		buf + (sizeof(*reply_packet)), 
		size,
		reply_packet->isdir, 
		ntohs(reply_packet->filebitmap), 
		ntohs(reply_packet->dirbitmap), 
		filecur);
	filecur->isdir=reply_packet->isdir;

	return 0;
}


int afp_getfiledirparms(struct afp_volume *volume, unsigned int did, unsigned int filebitmap, unsigned int dirbitmap, const char * pathname,
	struct afp_file_info *fpp)
{
	struct {
		struct dsi_header dsi_header __attribute__((__packed__));
		uint8_t command;
		uint8_t pad;
		uint16_t volid;
		uint32_t did;
		uint16_t file_bitmap;
		uint16_t directory_bitmap;
	}  __attribute__((__packed__)) * getfiledirparms;
	struct afp_server * server = volume->server;
	int ret = 0;
	char *path, * msg;
	unsigned int len;

	if (!pathname) return -1;

	len = sizeof(*getfiledirparms)+sizeof_path_header(server)+strlen(pathname);

	if ((msg = malloc(len))==NULL) 
		return -1;

	path = msg + (sizeof(*getfiledirparms));
	getfiledirparms=(void *) msg;

	dsi_setup_header(server,&getfiledirparms->dsi_header,DSI_DSICommand);
	getfiledirparms->command=afpGetFileDirParms;
	getfiledirparms->pad=0;
	getfiledirparms->volid=htons(volume->volid);
	getfiledirparms->did=htonl(did);
	getfiledirparms->file_bitmap=htons(filebitmap);
	getfiledirparms->directory_bitmap=htons(dirbitmap);
	copy_path(server,path,pathname,strlen(pathname));
	unixpath_to_afppath(server,path);

	ret=dsi_send(server, (char *) getfiledirparms,len,DSI_DEFAULT_TIMEOUT, 
		afpGetFileDirParms,(void *) fpp);

	free(msg);
	
	return ret;
}

int afp_createfile(struct afp_volume * volume, unsigned char flag, 
	unsigned int did, 
	char * pathname)
{
	struct {
		struct dsi_header dsi_header __attribute__((__packed__));
		uint8_t command;
		uint8_t flag;
		uint16_t volid;
		uint32_t did;
	}  __attribute__((__packed__)) * request_packet;
	char * path, *msg;
	struct afp_server * server=volume->server;
	unsigned int len = sizeof(*request_packet)+
		sizeof_path_header(server)+strlen(pathname);
	int ret;

	if ((msg = malloc(len))==NULL) 
		return -1;
	path = msg + (sizeof(*request_packet));
	request_packet =(void *) msg;
	dsi_setup_header(server,&request_packet->dsi_header,DSI_DSICommand);

	request_packet->command=afpCreateFile;
	request_packet->flag=flag;
	request_packet->volid=htons(volume->volid);
	request_packet->did=htonl(did);
	copy_path(server,path,pathname,strlen(pathname));
	unixpath_to_afppath(server,path);

	ret=dsi_send(server, (char *) request_packet,len,DSI_DEFAULT_TIMEOUT, 
		afpCreateFile,NULL);

	free(msg);
	
	return ret;
}

int afp_write(struct afp_volume * volume, unsigned short forkid,
	uint32_t offset, uint32_t reqcount, 
	char * data,uint32_t * written)
{
	struct {
		struct dsi_header dsi_header __attribute__((__packed__));
		uint8_t command;
		uint8_t flag;
		uint16_t forkid;
		uint32_t offset;
		uint32_t reqcount;
	}  __attribute__((__packed__)) * request_packet;
	struct afp_server * server = volume->server;

	unsigned int len = sizeof(*request_packet)+
		reqcount;
	int ret;
	char * dataptr, * msg;

	if ((msg = malloc(len))==NULL) 
		return -1;

	request_packet =(void *) msg;
	dataptr=msg+(sizeof(*request_packet));
	memcpy(dataptr,data,reqcount);
	dsi_setup_header(server,&request_packet->dsi_header,DSI_DSIWrite);
	request_packet->dsi_header.return_code.data_offset=htonl(sizeof(*request_packet)-sizeof(struct dsi_header));
	/* For writing data, set the offset correctly */
	request_packet->command=afpWrite;
	request_packet->flag=0;  /* we'll always do this from the start */
	request_packet->forkid=htons(forkid);
	request_packet->offset=htonl(offset);
	request_packet->reqcount=htonl(reqcount);
	ret=dsi_send(server, (char *) request_packet,len,DSI_DEFAULT_TIMEOUT, 
		afpWrite,(void *) written);

	free(msg);
	
	return ret;
}


int afp_write_reply(struct afp_server *server, char * buf, unsigned int size,
	void * other)
{
	uint32_t * written = other;
	struct {
		struct dsi_header header __attribute__((__packed__));
		uint64_t written;
	}  __attribute__((__packed__)) * reply_packet = (void *) buf;

	if (size<sizeof(*reply_packet)) 
		*written=0;
	else 
		*written=ntohl(reply_packet->written);

	return 0;
}

int afp_writeext(struct afp_volume * volume, unsigned short forkid,
	uint64_t offset, uint64_t reqcount, 
	char * data,uint64_t * written)
{
	struct {
		struct dsi_header dsi_header __attribute__((__packed__));
		uint8_t command;
		uint8_t flag;
		uint16_t forkid;
		uint64_t offset;
		uint64_t reqcount;
	}  __attribute__((__packed__)) * request_packet;
	struct afp_server * server = volume->server;

	unsigned int len = sizeof(*request_packet)+
		reqcount;
	int ret;
	char * dataptr, * msg;

	if ((msg = malloc(len))==NULL) 
		return -1;

	request_packet =(void *) msg;
	dataptr=msg+(sizeof(*request_packet));
	memcpy(dataptr,data,reqcount);
	dsi_setup_header(server,&request_packet->dsi_header,DSI_DSIWrite);
	request_packet->dsi_header.return_code.data_offset=htonl(sizeof(*request_packet)-sizeof(struct dsi_header));
	/* For writing data, set the offset correctly */
	request_packet->command=afpWriteExt;
	request_packet->flag=0;  /* we'll always do this from the start */
	request_packet->forkid=htons(forkid);
	request_packet->offset=hton64(offset);
	request_packet->reqcount=hton64(reqcount);
	ret=dsi_send(server, (char *) request_packet,len,DSI_DEFAULT_TIMEOUT, 
		afpWriteExt,(void *) written);

	free(msg);
	
	return ret;
}


int afp_writeext_reply(struct afp_server *server, char * buf, unsigned int size,
	void * other)
{
	uint64_t * written = other;
	struct {
		struct dsi_header header __attribute__((__packed__));
		uint64_t written;
	}  __attribute__((__packed__)) * reply_packet = (void *) buf;

	if (size<sizeof(*reply_packet)) 
		*written=0;
	else 
		*written=ntoh64(reply_packet->written);

	return 0;
}

