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


#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include <signal.h>
#include <iconv.h>

#include "afpfs-ng/utils.h"
#include "afpfs-ng/dsi.h"
#include "afpfs-ng/afp.h"
#include "afpfs-ng/uams_def.h"
#include "dsi_protocol.h"
#include "afpfs-ng/libafpclient.h"
#include "afp_internal.h"
#include "afp_replies.h"

/* define this in order to get reams of DSI debugging information */
#undef DEBUG_DSI

static int dsi_remove_from_request_queue(struct afp_server *server,
	struct dsi_request *toremove);
int convert_utf8dec_to_utf8pre(const char *src, int src_len,
	char * dest, int dest_len);
int convert_utf8pre_to_utf8dec(const char * src, int src_len, 
	char * dest, int dest_len);

/* This sets up a DSI header. */
void dsi_setup_header(struct afp_server * server, struct dsi_header * header, char command) 
{

	memset(header,0, sizeof(struct dsi_header));

	pthread_mutex_lock(&server->requestid_mutex);
	if (server->lastrequestid == 65535) server->lastrequestid = 0;
	else server->lastrequestid++;
	server->expectedrequestid = server->lastrequestid;

	pthread_mutex_unlock(&server->requestid_mutex);

	header->requestid = htons(server->lastrequestid);

	header->command = command;

}

int dsi_getstatus(struct afp_server * server)
{
#define GETSTATUS_BUF_SIZE 1024
	struct dsi_header  header;
	struct afp_rx_buffer rx;
	int ret;
	rx.data=malloc(GETSTATUS_BUF_SIZE);
	if(rx.data == NULL)
		return -1;

	rx.maxsize=GETSTATUS_BUF_SIZE;
	rx.size=0;
	dsi_setup_header(server,&header,DSI_DSIGetStatus);
	/* We're intentionally ignoring the results */
	ret=dsi_send(server,(char *) &header,sizeof(struct dsi_header),60,
		0,(void *) &rx);

	free(rx.data);
	return ret;
}

int dsi_sendtickle(struct afp_server *server)
{
	struct dsi_header  header;
	dsi_setup_header(server,&header,DSI_DSITickle);
	dsi_send(server,(char *) &header,sizeof(struct dsi_header),1,
		DSI_DONT_WAIT,NULL);
	return 0;
}

int dsi_opensession(struct afp_server *server)
{
	struct {
		struct dsi_header dsi_header  __attribute__((__packed__));
		uint8_t flags;
		uint8_t length;
		uint32_t rx_quantum ;
	} __attribute__((__packed__)) dsi_opensession_header;

	dsi_setup_header(server,&dsi_opensession_header.dsi_header,DSI_DSIOpenSession);
	/* Advertize our rx quantum */
	dsi_opensession_header.flags=1; 
	dsi_opensession_header.length=sizeof(unsigned int);
	dsi_opensession_header.rx_quantum=htonl(server->attention_quantum);

	dsi_send(server,(char *) &dsi_opensession_header,
		sizeof(dsi_opensession_header),1,DSI_BLOCK_TIMEOUT,NULL);
	return 0;
}

/*
static int check_incoming_dsi_message(struct afp_server * server, void * data, int size)
{
	struct dsi_header * header = (struct dsi_header *) data;

	if (size > sizeof(struct dsi_header)) {
		log_for_client(NULL,AFPFSD,LOG_WARNING,
			"DSI packet too small");
		return -1;
	}

	if (header->flags != DSI_REPLY) {
		log_for_client(NULL,AFPFSD,LOG_WARNING,
			"Got a non-DSI reply");
		return -1;
	}

	if (header->requestid < server->lastrequestid ) {
		log_for_client(NULL,AFPFSD,LOG_WARNING,
			"Got a requestid that was too low");
		return -1;
	}
	if (header->requestid > server->lastrequestid ) {
		log_for_client(NULL,AFPFSD,LOG_WARNING,
			"Got a requestid that was too high");
		return -1;
	}

	return 0;
}
*/

static int dsi_remove_from_request_queue(struct afp_server *server,
	struct dsi_request *toremove)
{

	struct dsi_request *p, *prev=NULL;
	#ifdef DEBUG_DSI
	printf("*** removing %d, %s\n",toremove->requestid, 
		afp_get_command_name(toremove->subcommand));
	#endif
	if (!server_still_valid(server)) return -1;
	pthread_mutex_lock(&server->request_queue_mutex);
	for (p=server->command_requests;p;p=p->next) {
		if (p==toremove) {
			if (prev==NULL)
				server->command_requests=p->next;
			else
				prev->next = p->next;
			server->stats.requests_pending--;
			free(p);
			pthread_mutex_unlock(&server->request_queue_mutex);
			return 0;
		}
		prev=p;
	}

	pthread_mutex_unlock(&server->request_queue_mutex);
	#ifdef DEBUG_DSI
	printf("*** Never removed anything for %d, %s\n",toremove->requestid,
		afp_get_command_name(toremove->subcommand));
	#endif
	log_for_client(NULL,AFPFSD,LOG_WARNING,
		"Got an unknown reply for requestid %i\n",ntohs(toremove->requestid));
	return -1;
}


int dsi_send(struct afp_server *server, char * msg, int size,int wait,unsigned char subcommand, void ** other) 
{
	/* For wait:
	 * -1: wait forever
	 *  0: don't wait
	 * x>n: wait for N seconds */

	struct dsi_header  *header = (struct dsi_header *) msg;
	struct dsi_request * new_request, *p;
	int rc=0;
	struct timespec ts;
	struct timeval tv;
 	header->length=htonl(size-sizeof(struct dsi_header));

	if (!server_still_valid(server) || server->fd==0)
		return -1;

	afp_wait_for_started_loop();

	/* Add request to the queue */
	if ((new_request=malloc(sizeof(struct dsi_request))) == NULL) {
		log_for_client(NULL,AFPFSD,LOG_ERR,
			"Could not allocate for new request\n");
		return -1;
	}
	memset(new_request,0,sizeof(struct dsi_request));
	new_request->requestid=server->lastrequestid;
	new_request->subcommand=subcommand;
	new_request->other=other;
	new_request->wait=wait;
	new_request->next=NULL;
      	new_request->done_waiting=0;

	pthread_mutex_lock(&server->request_queue_mutex);
	if (server->command_requests==NULL) {
		server->command_requests=new_request;
	} else {
		for (p=server->command_requests;p->next;p=p->next);
		p->next=new_request;
	}
	server->stats.requests_pending++;
	pthread_mutex_unlock(&server->request_queue_mutex);

	pthread_cond_init(&new_request->waiting_cond,NULL);
	pthread_mutex_init(&new_request->waiting_mutex,NULL);

	if (server->connect_state==SERVER_STATE_DISCONNECTED) {
		char mesg[1024];
		unsigned int l=0; 
		/* Try and reconnect */

		afp_server_reconnect(server,mesg,&l,1024);


	}

	pthread_mutex_lock(&server->send_mutex);
	#ifdef DEBUG_DSI
	printf("*** Sending %d, %s\n",ntohs(header->requestid),
		afp_get_command_name(new_request->subcommand));
	#endif
	if (write(server->fd,msg,size)<0) {
		if ((errno==EPIPE) || (errno==EBADF)) {
			/* The server has closed the connection */
			server->connect_state=SERVER_STATE_DISCONNECTED;
			return -1;

		}
		perror("writing to server");
		rc=-1;
		pthread_mutex_unlock(&server->send_mutex);
		goto out;
	}
	server->stats.tx_bytes+=size;
	pthread_mutex_unlock(&server->send_mutex);

	#ifdef DEBUG_DSI
	printf("=== Waiting for response for %d %s\n",
		new_request->requestid,
		afp_get_command_name(new_request->subcommand));
	#endif
	if (new_request->wait<0) {

		/* Wait forever */
		#ifdef DEBUG_DSI
		printf("=== Waiting forever for %d, %s\n",
			new_request->requestid,
			afp_get_command_name(new_request->subcommand));
		#endif

		pthread_mutex_lock(&new_request->waiting_mutex);

		if (new_request->done_waiting==0)
			rc=pthread_cond_wait( 
				&new_request->waiting_cond, 
					&new_request->waiting_mutex );

		pthread_mutex_unlock(&new_request->waiting_mutex);

	} else if (new_request->wait>0) {
		/* wait for new_request->wait seconds */

		#ifdef DEBUG_DSI
		printf("=== Waiting for %d %s, for %ds\n",
			new_request->requestid,
			afp_get_command_name(new_request->subcommand),
			new_request->wait);
		#endif

		gettimeofday(&tv,NULL);
		ts.tv_sec=tv.tv_sec;
		ts.tv_sec+=new_request->wait;
		ts.tv_nsec=tv.tv_usec *1000;
		if (new_request->wait==0) {
			#ifdef DEBUG_DSI
			printf("=== Changing my mind, no longer waiting for %d\n",
				new_request->requestid);
			#endif
			goto skip;
		}
		pthread_mutex_lock(&new_request->waiting_mutex);
		if (new_request->done_waiting==0) 
			rc=pthread_cond_timedwait( 
				&new_request->waiting_cond, 
				&new_request->waiting_mutex,&ts);
		pthread_mutex_unlock(&new_request->waiting_mutex);

		if (rc==ETIMEDOUT) {
/* FIXME: should handle this case properly */
			#ifdef DEBUG_DSI
			printf("=== Timedout for %d\n",
				new_request->requestid);
			#endif
			goto out;
		}
	} else {
                /* Don't wait */
		#ifdef DEBUG_DSI
		printf("=== Skipping wait altogether for %d\n",new_request->requestid);
		#endif
	}
	#ifdef DEBUG_DSI
	printf("=== Done waiting for %d %s, waiting for %ds,"
		" return %d, DSI return %d\n",
		new_request->requestid,
		afp_get_command_name(new_request->subcommand),
		new_request->wait, 
		rc,new_request->return_code);
	#endif
skip:
	rc=new_request->return_code;
out:
	dsi_remove_from_request_queue(server,new_request);
	return rc;
}

int dsi_command_reply(struct afp_server* server,unsigned short subcommand, void * other) {

	int ret = 0;

	if (server->data_read<sizeof(struct dsi_header)) {
		log_for_client(NULL,AFPFSD,LOG_WARNING,
		"Got a short reply command, I am just ignoring it. size: %d\n",server->data_read);
		return -1;
	}


	if (subcommand==0) {
		log_for_client(NULL,AFPFSD,LOG_WARNING,
			"Broken subcommand: %d\n",subcommand);
		return -1;
	}

	if ((subcommand==afpRead) || ( subcommand==afpReadExt)) {
		struct afp_rx_buffer * buf = other;
		#ifdef DEBUG_DSI
		printf("=== read() for afpRead, %d bytes\n",buf->maxsize-buf->size);
		#endif
		if ((ret=read(server->fd,buf->data+buf->size,
			buf->maxsize-buf->size))<0) {
			return -1;
		}
		server->stats.rx_bytes+=ret;
		if (ret==0) {
			return -1;
		}
		server->data_read+=ret;
	}

	ret = afp_reply(subcommand,server,other);
	return ret;
}


void dsi_opensession_reply(struct afp_server * server) {

	struct {
		uint8_t flags ;
		uint8_t length ;
		uint32_t tx_quantum;
	}  __attribute__((__packed__)) * dsi_opensession_header = (void *) 
		server->incoming_buffer + sizeof(struct dsi_header);

	server->tx_quantum = ntohl(dsi_opensession_header->tx_quantum);

}

static int dsi_parse_versions(struct afp_server * server, char * msg) 
{
	unsigned char num_versions = msg[0];
	int i,j=0;
	char * p;
	unsigned char len;
	char tmpversionname[33];
	struct afp_versions * tmpversion;	

	memset(server->versions,0, SERVER_MAX_VERSIONS);

	if (num_versions > SERVER_MAX_VERSIONS) num_versions = SERVER_MAX_VERSIONS;
	p=msg+1;
	for (i=0;i<num_versions;i++) {
		len=copy_from_pascal(tmpversionname,p,33)+1;
		for (tmpversion=afp_versions;tmpversion->av_name;tmpversion++) {
			if (strcmp(tmpversion->av_name,tmpversionname)==0) {
				server->versions[j]=tmpversion->av_number;
				j++;
				break;
			}
		}
		p+=len;
	}
	return 0;
}

static int dsi_parse_uams(struct afp_server * server, char * msg) 
{
	unsigned char num_uams = msg[0];
	unsigned char len;
	int i;
	char * p;
	char ua_name[AFP_UAM_LENGTH+1];

	server->supported_uams= 0;

	memset(ua_name,0,AFP_UAM_LENGTH+1);

	if (num_uams > SERVER_MAX_UAMS) num_uams = SERVER_MAX_UAMS;
	p=msg+1;
	for (i=0;i<num_uams;i++) {
		len=copy_from_pascal(ua_name,p,AFP_UAM_LENGTH)+1;
		server->supported_uams|=uam_string_to_bitmap(ua_name);
		p+=len;
	}
	
	return 0;

}


/* The parsing of the return for DSI GetStatus is the same as for 
 * AFP GetSrvrInfo (which we don't yet support) */

void dsi_getstatus_reply(struct afp_server * server) 
{
	/* Todo: check for buffer overruns */

	char * data, *p, *p2;
	int len;
	uint16_t * offset;

	/* This is the fixed portion */
	struct dsi_getstatus_header {
		struct dsi_header dsi __attribute__((__packed__));
		uint16_t machine_offset;
		uint16_t version_offset;
		uint16_t uams_offset;
		uint16_t icon_offset;
		uint16_t flags ;
	} __attribute__((__packed__)) * reply1 = (void *) server->incoming_buffer;

	struct reply2 {
		uint16_t signature_offset;
		uint16_t networkaddress_offset;
		uint16_t directoryservices_offset;
		uint16_t utf8servername_offset;
	} __attribute__((__packed__)) * reply2;

	if (server->data_read < (sizeof(*reply1) + sizeof(*reply2))) {
		log_for_client(NULL,AFPFSD,LOG_ERR,
			"Got incomplete data for getstatus\n");
		return ;
	}

	data = (char * ) server->incoming_buffer + sizeof(struct dsi_header);

	/* First, get the fixed portion */
	p=data + ntohs(reply1->machine_offset);
	copy_from_pascal(server->machine_type,p,AFP_MACHINETYPE_LEN);

	p=data + ntohs(reply1->version_offset);
	dsi_parse_versions(server, p);

	p=data + ntohs(reply1->uams_offset);
	dsi_parse_uams(server, p);

	if (ntohs(reply1->icon_offset)>0) {
		/* The icon and mask are optional */
		p=data + ntohs(reply1->icon_offset);
		memcpy(server->icon,p,256);
	}
	server->flags=ntohs(reply1->flags);

	p=(void *)((unsigned long) server->incoming_buffer + sizeof(*reply1));
	p+=copy_from_pascal(server->server_name,p,AFP_SERVER_NAME_LEN)+1;

	/* Now work our way through the variable bits */

        /* First, make sure we're on an even boundary */
        if (((uintptr_t) p) & 0x1) p++;

	/* Get the signature */

	offset = (uint16_t *) p;
	memcpy(server->signature,
        	((void *) data)+ntohs(*offset),
		AFP_SIGNATURE_LEN);
	p+=2;

	/* The network addresses */
	if (server->flags & kSupportsTCP) {
		offset = (uint16_t *) p;
		/* We don't actually do anything with the network addresses,
		 * but if we did, it'd go here */
		p+=2;
	}
	/* The directory names */
	if (server->flags & kSupportsDirServices) {
		offset = (uint16_t *) p;
		/* We don't actually do anything with the directory names,
		 * but if we did, it'd go here */
		p+=2;
	}
	if (server->flags & kSupportsUTF8SrvrName) {

		/* And now the UTF8 server name */
		offset = (uint16_t *) p;

		p2=((void *) data)+ntohs(*offset);

		/* Skip the hint character */
		p2+=1;
		len=copy_from_pascal(server->server_name_utf8,p2,
			AFP_SERVER_NAME_UTF8_LEN);

		/* This is a workaround.  There's a bug in netatalk that in some
		 * circumstances puts the UTF8 servername off by one character */
		if (len==0) {
			p2++;
			len=copy_from_pascal(server->server_name_utf8,p2,
				AFP_SERVER_NAME_UTF8_LEN);
		}

		convert_utf8dec_to_utf8pre(server->server_name_utf8,
			strlen(server->server_name_utf8),
			server->server_name_printable, AFP_SERVER_NAME_UTF8_LEN);
	} else {
		/* We don't have a UTF8 servername, so let's make one */

		iconv_t cd;
		size_t inbytesleft = strlen(server->server_name);
		size_t outbytesleft = AFP_SERVER_NAME_UTF8_LEN;
		char * inbuf = server->server_name;
		char * outbuf = server->server_name_printable;

		if ((cd  = iconv_open("MACINTOSH","UTF-8")) == (iconv_t) -1)
			return;

		iconv(cd,&inbuf,&inbytesleft,
			&outbuf, &outbytesleft);
		iconv_close(cd);

	}
}


void dsi_incoming_closesession(struct afp_server *server)
{
	afp_unmount_all_volumes(server);
	loop_disconnect(server);
}

void dsi_incoming_tickle(struct afp_server * server) 
{
	struct dsi_header  header;

	dsi_setup_header(server,&header,DSI_DSITickle);
	dsi_send(server,(char *) &header,sizeof(struct dsi_header),0,
		DSI_DONT_WAIT,NULL);

}


void * dsi_incoming_attention(void * other)
{
	struct afp_server * server = other;
	struct {
		struct dsi_header header __attribute__((__packed__));
		uint16_t flags ;
	} __attribute__((__packed__)) *packet = (void *) server->attention_buffer;
	unsigned short flags;
	char mesg[AFP_LOGINMESG_LEN];
	unsigned char shutdown=0;
	unsigned char mins=0;
	unsigned char checkmessage=0;

	memset(mesg,0,AFP_LOGINMESG_LEN);

	/* The logic here's undocumented.  If we get an attention packet and
	   there's no flag, then go check the message.  Also, go check the
	   the message if there is a flag and we have the AFPATTN_MESG flag.
	   Checked on netatalk 2.0.3. */

	/* It's a bit tough to find docs on this, but I found it at:

	http://web.archive.org/web/20010806173437/developer.apple.com/techpubs/macosx/Networking/AFPClient/AFPClient-15.html

	*/

	if (ntohl(packet->header.length)>=2) {
		flags=ntohs(packet->flags);

		if (flags&AFPATTN_MESG)
			checkmessage=1;
		if (flags&(AFPATTN_CRASH|AFPATTN_SHUTDOWN))
			shutdown=1;
		mins=flags & 0xff;
	} else {
		checkmessage=1;
	}

	if (checkmessage) {
		afp_getsrvrmsg(server,AFPMESG_SERVER,
			((server->using_version->av_number>=30)?1:0),
			DSI_DEFAULT_TIMEOUT,mesg); 
		if(bcmp(mesg,"The server is going down for maintenance.",41)==0)
			shutdown=1;
	}

	if (shutdown) {
		log_for_client(NULL,AFPFSD,LOG_ERR,
			"Got a shutdown notice in packet %d, going down in %d mins\n",ntohs(packet->header.requestid),mins);
		loop_disconnect(server);
		server->connect_state=SERVER_STATE_DISCONNECTED;
		return NULL;
	}
	return NULL;
}


struct dsi_request * dsi_find_request(struct afp_server *server,
	unsigned short request_id)
{

	struct dsi_request *p, *prev=NULL;

	pthread_mutex_lock(&server->request_queue_mutex);
	for (p=server->command_requests;p;p=p->next) {
		if (request_id==p->requestid) {
			pthread_mutex_unlock(&server->request_queue_mutex);
			return p;
		}
		prev=p;
	}
	pthread_mutex_unlock(&server->request_queue_mutex);

	return NULL;
}

int dsi_recv(struct afp_server * server) 
{
	struct dsi_header * header = (void *) server->incoming_buffer;
	struct dsi_request * request=NULL;
	int rc;
        int amount_to_read=0;
	int ret;
	unsigned char runt_packet=0;
	/* Make sure we have at least one  header */
	if ((amount_to_read=sizeof(struct dsi_header)-server->data_read)>0) {
		#ifdef DEBUG_DSI
		printf("<<< read() for dsi, %d bytes\n",amount_to_read);
		#endif
		ret = read(server->fd,server->incoming_buffer+server->data_read,
			amount_to_read);
		if (ret<0) {
			perror("dsi_recv");
			return -1;
		}
		if (ret==0) {
			return -1;
		}
		server->stats.rx_bytes+=ret;
		server->data_read+=ret;
		if ((server->data_read==sizeof(struct dsi_header)) && 
			(ntohl(header->length)==0)) {
			goto gotenough;
			}
		return 0;
			/* We'll get the rest of the packet next time */
	}
gotenough:
	/* At this point, we have just the header */
	/* Figure out what it is a reply to */
	request = dsi_find_request(server,ntohs(header->requestid));
	if (!request && (header->flags==DSI_REPLY)) {
		log_for_client(NULL,AFPFSD,LOG_ERR,
			"I have no idea what this is a reply to, id %d.\n",
			ntohs(header->requestid));
			runt_packet=1;
			server->stats.runt_packets++;
	}
	if (request) request->return_code=ntohl(header->return_code.error_code);

	/* If it is a read, read in as much as we can */
	if ((request) && 
		((request->subcommand==afpRead) || 
		(request->subcommand==afpReadExt))) {
		struct afp_rx_buffer * buf = request->other;
		int newmax=buf->maxsize-buf->size;

		if (((server->data_read==sizeof(struct dsi_header)) &&
			(ntohl(header->length)==0))) {
				server->data_read=0;
				goto out;
			}

		if ((!buf) || (!buf->maxsize)) {
			log_for_client(NULL,AFPFSD,LOG_ERR,
				"No buffer allocated for incoming data\n");
			return -1;
		}
		if (newmax>ntohl(header->length)-buf->size)
			newmax=ntohl(header->length)-buf->size;

		#ifdef DEBUG_DSI
		printf("<<< read() in response to a request, %d bytes\n",newmax);
		#endif
		ret = read(server->fd,buf->data+buf->size,
			newmax);
		if (ret<0) {
			return -1;
		}
		if (ret==0) {
			return -1;
		}
		server->stats.rx_bytes+=ret;
		buf->size+=ret;

		/* Check to see if we've read enough */
		if (ntohl(header->length)==buf->size) {
			char * tmpbuf;
			int size_to_copy=server->data_read
				-sizeof(struct dsi_header);
			if (size_to_copy==0) {
				server->data_read=0;
				goto out;
			} else if (size_to_copy<0) goto error;


			/* Okay, so there is a buffer we have to shift */
			if ((tmpbuf=malloc(size_to_copy))==NULL) {
				log_for_client(NULL,AFPFSD,LOG_ERR,
					"Problem allocating memory for dsi_recv of size %d",size_to_copy);
				goto error;
			}

			memcpy(tmpbuf,
				server->incoming_buffer+
				sizeof(struct dsi_header), 
				size_to_copy);
			memcpy(server->incoming_buffer,tmpbuf,
				size_to_copy);
			server->data_read=size_to_copy;
			free(tmpbuf);
		} else return 0;
	} else {
		/* Okay, so it isn't a response to an afpRead or afpReadExt */

		if (((server->data_read==sizeof(struct dsi_header)) &&
			(ntohl(header->length)==0))) 
				goto process_packet;

		amount_to_read=min(ntohl(header->length),server->bufsize);
		#ifdef DEBUG_DSI
		printf("<<< read() of rest of AFP, %d bytes\n",amount_to_read);
		#endif
		ret = read(server->fd, (void *)
		(((unsigned long) server->incoming_buffer)+server->data_read),
			amount_to_read);
		if (ret<0) return -1;
		if (ret==0) {
			return -1;
		}
		server->stats.rx_bytes+=ret;
		server->data_read+=ret;

		if (server->data_read<(ntohl(header->length)+sizeof(*header)))
			return 0;
	}
	if (runt_packet) 
		goto after_processing;

process_packet:
	/* At this point, we have a full DSI packet
	   (or, for an afpRead, just the header and the data's been
	    dumped in the preallocated buffer */
	#ifdef DEBUG_DSI
	printf("<<< Handling %d\n",ntohs(header->requestid));
	#endif

	switch (header->command) {

	case DSI_DSICloseSession:
		dsi_incoming_closesession(server);
		break;
	case DSI_DSIGetStatus:
		dsi_getstatus_reply(server);
		break;
	case DSI_DSIOpenSession:
		dsi_opensession_reply(server);
		break;
	case DSI_DSITickle:
		dsi_incoming_tickle(server);
		break;
	case DSI_DSIWrite:
	case DSI_DSICommand:
		if (!runt_packet) 
		dsi_command_reply(server, request->subcommand,request->other);
		break;
	case DSI_DSIAttention:
		{
			pthread_t thread;
			memcpy( server->attention_buffer,
				server->incoming_buffer,
				server->data_read);
			server->attention_len=server->data_read;
			pthread_create(&thread,NULL,
				dsi_incoming_attention,server);
		}
		break;
	default:
		log_for_client(NULL,AFPFSD,LOG_ERR,
			"Unknown DSI command %i\n",header->command);
		goto error;

	}
after_processing:
	if  (server->data_read==ntohl(header->length)+sizeof(*header)) {
		/* The most common case */
		server->data_read=0;
	} else {
		unsigned int size_to_copy=
			server->data_read-
			(ntohl(header->length)+sizeof(*header));
		char * tmpbuf;

		if (size_to_copy<ntohl(header->length)) {
			/* This could be replaced with memmove, as it handles
			 * overlaps */
			memcpy( server->incoming_buffer,
				server->incoming_buffer+ntohl(header->length),
				size_to_copy);
		} else {
			/* This is more complicated, we need an tmp buf */
			if ((tmpbuf=malloc(size_to_copy))==NULL) {
				log_for_client(NULL,AFPFSD,LOG_ERR,
					"Problem allocating memory for dsi_recv of size %d",size_to_copy);
				goto error;
			}

			memcpy(tmpbuf,
				server->incoming_buffer+ntohl(header->length),
				size_to_copy);

			memcpy(server->incoming_buffer,tmpbuf,size_to_copy);
			free(tmpbuf);
		}
		server->data_read-=size_to_copy;
	}
			
out:

	rc=ntohl(header->return_code.error_code);
	if (request) {
		#ifdef DEBUG_DSI
		printf("<<< Found request %d, %s\n",request->requestid,
			afp_get_command_name(request->subcommand));
		#endif
		if (request->wait) {
			#ifdef DEBUG_DSI
			printf("<<< Signalling %d, returning %d or %d\n",request->requestid,request->return_code,rc);
			#endif
			pthread_mutex_lock(&request->waiting_mutex);
			request->wait=0;
			request->done_waiting=1;
			pthread_cond_signal(&request->waiting_cond);
			pthread_mutex_unlock(&request->waiting_mutex);
		} else {
			dsi_remove_from_request_queue(server,request);
		}
	}
	return 0;
error:
	#ifdef DEBUG_DSI
	printf("returning from dsi_recv with an error\n");
	#endif
	return -1;


}


