/*
 *  sock.c
 *
 *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
 *  Copyright (C) 1997 by Volker Lendecke
 *
 *  Please add a note about your changes to davfs in the ChangeLog file.
 */

#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/mm.h>
#include <linux/netdevice.h>
#include <linux/smp_lock.h>
#include <net/scm.h>
#include <net/ip.h>
#include <linux/poll.h>
#include <asm/uaccess.h>

#include "davfs.h"
#include "dav_debug.h"


int
_recvfrom(struct socket *socket, unsigned char *ubuf, int size,
	  unsigned flags)
{
	struct iovec iov;
	struct msghdr msg;
	struct scm_cookie scm;

	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = NULL;
	iov.iov_base = ubuf;
	iov.iov_len = size;
	
	memset(&scm, 0,sizeof(scm));
	size=socket->ops->recvmsg(socket, &msg, size, flags, &scm);
	if(size>=0)
		scm_recv(socket,&msg,&scm,flags);
	return size;
}

static int
_send(struct socket *socket, const void *buff, int len)
{
	struct iovec iov;
	struct msghdr msg;
	struct scm_cookie scm;
	int err;

	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = NULL;
	msg.msg_controllen = 0;
	
	iov.iov_base = (void *)buff;
	iov.iov_len = len;

	msg.msg_flags = 0;

	err = scm_send(socket, &msg, &scm);
        if (err >= 0)
	{
		err = socket->ops->sendmsg(socket, &msg, len, &scm);
		scm_destroy(&scm);
	}
	return err;
}

struct data_callback {
	struct tq_struct cb;
	struct sock *sk;
};


/*
 * get socket struct 
 * from mount's fd
 *
 * return NULL, if fail
 */
struct socket *
dav_get_sock(struct dav_sb_info *server, unsigned int fd)
{
    struct inode *sock_inode;
    
    server->sock_file = fget(fd);
    if(!server->sock_file) 
	return NULL;
    
    sock_inode = server->sock_file->f_dentry->d_inode;
    if(!S_ISSOCK(sock_inode->i_mode)) {
	fput(server->sock_file);
	return NULL;
    }
    
    server->sock = &sock_inode->u.socket_i;
    if(!server->sock) 
	fput(server->sock_file);

    return server->sock;
}


/*
 * Called with the server locked.
 */
void
dav_close_socket(struct dav_sb_info *server)
{
    struct file * file = server->sock_file;
    
    if (file) {
	server->sock_file = NULL;
	fput(file);
    }
}

int
dav_send_raw(struct socket *socket, unsigned char *source, int length)
{
	int result;
	int already_sent = 0;

	while (already_sent < length)
	{
		result = _send(socket,
			       (void *) (source + already_sent),
			       length - already_sent);

		if (result == 0)
		{
			return -EIO;
		}
		if (result < 0)
		{
			DEBUG1("dav_send_raw: sendto error = %d\n", -result);
			return result;
		}
		already_sent += result;
	}
	return already_sent;
}

int
dav_receive_raw(struct socket *socket, unsigned char *target, int length)
{
	int result;
	int already_read = 0;

	while (already_read < length)
	{
	    result = _recvfrom(socket,
			       (void *) (target + already_read),
			       length - already_read, 0);
	    if (result == 0)
	      {
		  return -EIO;
	      }
	    if (result < 0)
	      {
		  DEBUG1("recvfrom error = %d\n", -result);
			return result;
	      }
	    already_read += result;
	}
	return already_read;
}


int do_tcp_rcv(struct dav_sb_info *server, void *buffer, size_t len) {
    struct poll_wqueues wait_table;
    struct socket *sock = server->sock;
    int init_timeout=10;
    size_t dataread;
    int result = 0;
    
    dataread = 0;
    /*
    init_timeout = server->m.time_out * 20;
    */
    /* hard-mounted volumes have no timeout, except connection close... */
    /*
    if (!(server->m.flags & NCP_MOUNT_SOFT))
	init_timeout = 0x7FFF0000;
    */
    while (len) {
	poll_initwait(&wait_table);
	/* mb() is not necessary because ->poll() will serialize
	   instructions adding the wait_table waitqueues in the
	   waitqueue-head before going to calculate the mask-retval. */
	__set_current_state(TASK_INTERRUPTIBLE);
	if (!(sock->ops->poll(server->sock_file, sock, &wait_table.pt) & POLLIN)) {
	    init_timeout = schedule_timeout(init_timeout);
	    poll_freewait(&wait_table);
	    current->state = TASK_RUNNING;
	    if (signal_pending(current)) {
		return -ERESTARTSYS;
	    }
	    if (!init_timeout) {
		return -EIO;
	    }
	    if(wait_table.error) {
		return wait_table.error;
	    }
	} else {
	    poll_freewait(&wait_table);
	}
	current->state = TASK_RUNNING;
	
	result = _recvfrom(sock, buffer, len, MSG_DONTWAIT);
	if (result < 0) {
	    if (result == -EAGAIN) {
		DEBUG1("ncpfs: tcp: bad select ready\n");
		continue;
	    }
	    return result;
	}
	if (result == 0) {
	    printk(KERN_ERR "ncpfs: tcp: EOF on socket\n");
	    return -EIO;
	}
	if (result > len) {
	    printk(KERN_ERR "ncpfs: tcp: bug in recvmsg\n");
	    return -EIO;			
	}
	dataread += result;
	buffer += result;
	len -= result;
    }
    return 0;
}	

int
dav_sendline_raw(struct socket *socket, unsigned char *target) {
    int ret = 0;
    ret+=dav_send_raw(socket, target, strlen(target));
    ret+=dav_send_raw(socket, "\r\n", 2);

    return ret;
}
									   
int
dav_readline_tcp(struct dav_sb_info *server, unsigned char *target, int length) {
    int ret = 0;
    int ch;
    
    for(ret=0;ret<length && do_tcp_rcv(server, &ch, 1)>0; ret++) {
	if(ch=='\r') {
	    /* read '\n' */
	    do_tcp_rcv(server, &ch, 1);
	    break;
	}
	*target++=ch;
    }   

    *target = 0;

    return ret;
}
									   
int
dav_readline_raw(struct dav_sb_info *server, unsigned char *target, int length) {
    int ret = 0;
    unsigned char ch;
    
    for(ret=0;ret<length && dav_receive_raw(server->sock, &ch, 1)>0; ret++) {
	if(ch=='\r' && ch=='\n') {
	    /* read '\n' */
	    //dav_receive_raw(server->sock, &ch, 1);
	    break;
	}
	*target++=ch;
    }   

    *target = 0;

    return ret;
}
									   











