/* 
   Netdrive Samba client plugin
   samba library wrappers
   Copyright (C) netlabs.org 2003-2008

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
*/

#include "includes.h"

#include "smbwrp.h"

#ifndef DEBUG_PRINTF
#define debug_printf( ...)
#endif

static int
net_share_enum_rpc(struct cli_state *cli,
                   void (*fn)(const char *name,
                              uint32 type,
                              const char *comment,
                              void *state),
                   void *state)
{
        int i;
	WERROR result;
	ENUM_HND enum_hnd;
        uint32 info_level = 1;
	uint32 preferred_len = 0xffffffff;
        uint32 type;
	SRV_SHARE_INFO_CTR ctr;
	fstring name = "";
        fstring comment = "";
        void *mem_ctx;
	struct rpc_pipe_client *pipe_hnd;
        NTSTATUS nt_status;

        /* Open the server service pipe */
        pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status);
        if (!pipe_hnd) {
                DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
                return -1;
        }

        /* Allocate a context for parsing and for the entries in "ctr" */
        mem_ctx = talloc_init("libsmbclient: net_share_enum_rpc");
        if (mem_ctx == NULL) {
                DEBUG(0, ("out of memory for net_share_enum_rpc!\n"));
                cli_rpc_pipe_close(pipe_hnd);
                return -1; 
        }

        /* Issue the NetShareEnum RPC call and retrieve the response */
	init_enum_hnd(&enum_hnd, 0);
	result = rpccli_srvsvc_net_share_enum(pipe_hnd,
                                              mem_ctx,
                                              info_level,
                                              &ctr,
                                              preferred_len,
                                              &enum_hnd);

        /* Was it successful? */
	if (!W_ERROR_IS_OK(result) || ctr.num_entries == 0) {
                /*  Nope.  Go clean up. */
		goto done;
        }

        /* For each returned entry... */
        for (i = 0; i < ctr.num_entries; i++) {

                /* pull out the share name */
                rpcstr_pull_unistr2_fstring(
                        name, &ctr.share.info1[i].info_1_str.uni_netname);

                /* pull out the share's comment */
                rpcstr_pull_unistr2_fstring(
                        comment, &ctr.share.info1[i].info_1_str.uni_remark);

                /* Get the type value */
                type = ctr.share.info1[i].info_1.type;

                /* Add this share to the list */
                (*fn)(name, type, comment, state);
        }

done:
        /* Close the server service pipe */
        cli_rpc_pipe_close(pipe_hnd);

        /* Free all memory which was allocated for this request */
        TALLOC_FREE(mem_ctx);

        /* Tell 'em if it worked */
        return W_ERROR_IS_OK(result) ? 0 : -1;
}

/*
 * Wrapper for cli_errno to return not connected error on negative fd
 * Now returns an OS/2 return code instead of lerrno.
 */
int os2cli_errno(cli_state * cli)
{
	if (cli->fd == -1)
	{
		return maperror( ENOTCONN);
	}
	return maperror(cli_errno(cli));
}

int _System smbwrp_getclisize(void)
{
	return sizeof(struct cli_state);
}

/***************************************************** 
initialise structures
*******************************************************/
int _System smbwrp_init(void)
{
	static int initialised = 0;
	char *p;
	pstring line;

	if (initialised) 
	{
		return 0;
	}
	initialised = 1;

	load_case_tables();

	init_globals( True);

	load_interfaces();

	if (!init_names())
	{
		return 1;
	}

/*
	if ((p=smbw_getshared("RESOLVE_ORDER"))) {
		lp_set_name_resolve_order(p);
	}
*/
	return 0;

}

#if 0
/***************************************************** 
remove redundent stuff from a filename
*******************************************************/
void clean_fname(char *name)
{
	char *p, *p2;
	int l;
	int modified = 1;

	if (!name) return;

	while (modified) {
		modified = 0;

		if ((p=strstr(name,"/./"))) {
			modified = 1;
			while (*p) {
				p[0] = p[2];
				p++;
			}
		}

		if ((p=strstr(name,"//"))) {
			modified = 1;
			while (*p) {
				p[0] = p[1];
				p++;
			}
		}

		if (strcmp(name,"/../")==0) {
			modified = 1;
			name[1] = 0;
		}

		if ((p=strstr(name,"/../"))) {
			modified = 1;
			for (p2=(p>name?p-1:p);p2>name;p2--) {
				if (p2[0] == '/') break;
			}
			while (*p2) {
				p2[0] = p2[3];
				p2++;
			}
		}

		if (strcmp(name,"/..")==0) {
			modified = 1;
			name[1] = 0;
		}

		l = strlen(name);
		p = l>=3?(name+l-3):name;
		if (strcmp(p,"/..")==0) {
			modified = 1;
			for (p2=p-1;p2>name;p2--) {
				if (p2[0] == '/') break;
			}
			if (p2==name) {
				p[0] = '/';
				p[1] = 0;
			} else {
				p2[0] = 0;
			}
		}

		l = strlen(name);
		p = l>=2?(name+l-2):name;
		if (strcmp(p,"/.")==0) {
			if (p == name) {
				p[1] = 0;
			} else {
				p[0] = 0;
			}
		}

		if (strncmp(p=name,"./",2) == 0) {      
			modified = 1;
			do {
				p[0] = p[2];
			} while (*p++);
		}

		l = strlen(p=name);
		if (l > 1 && p[l-1] == '/') {
			modified = 1;
			p[l-1] = 0;
		}
	}
}
#endif


/****************************************************************************
send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level
****************************************************************************/
BOOL cli_qpathinfo3(struct cli_state *cli, const char *fname, 
		    time_t *c_time, time_t *a_time, time_t *m_time, 
		    time_t *w_time, off_t *size, uint16 *mode,
		    SMB_INO_T *ino)
{
	unsigned int data_len = 0;
	unsigned int param_len = 0;
	uint16 setup = TRANSACT2_QPATHINFO;
	pstring param;
	char *rparam=NULL, *rdata=NULL;
	char *p;

	p = param;
	memset(p, 0, 6);
	SSVAL(p, 0, SMB_QUERY_FILE_ALL_INFO);
	p += 6;
	p += clistr_push(cli, p, fname, sizeof(pstring)-6, STR_TERMINATE);

	param_len = PTR_DIFF(p, param);

	if (!cli_send_trans(cli, SMBtrans2, 
                            NULL,                         /* name */
                            -1, 0,                        /* fid, flags */
                            &setup, 1, 0,                 /* setup, length, max */
                            param, param_len, 10,         /* param, length, max */
                            NULL, data_len, cli->max_xmit /* data, length, max */
                           )) {
		return False;
	}

	if (!cli_receive_trans(cli, SMBtrans2,
                               &rparam, &param_len,
                               &rdata, &data_len)) {
		return False;
	}

	if (!rdata || data_len < 22) {
		return False;
	}

	if (c_time) {
		*c_time = convert_timespec_to_time_t(interpret_long_date(rdata+0));
	}
	if (a_time) {
		*a_time = convert_timespec_to_time_t(interpret_long_date(rdata+8));
	}
	if (m_time) {
		*m_time = convert_timespec_to_time_t(interpret_long_date(rdata+16));
	}
	if (w_time) {
		*w_time = convert_timespec_to_time_t(interpret_long_date(rdata+24));
	}
	if (mode) {
		*mode = SVAL(rdata, 32);
	}
	if (size) {
		*size = IVAL2_TO_SMB_BIG_UINT(rdata, 48);
	}
	if (ino) {
		*ino = IVAL(rdata, 64);
	}

	SAFE_FREE(rdata);
	SAFE_FREE(rparam);
	return True;
}

/****************************************************************************
send a qfileinfo call
****************************************************************************/
BOOL cli_qfileinfo3(struct cli_state *cli, int fnum, 
		   uint16 *mode, off_t *size,
		   time_t *c_time, time_t *a_time, time_t *m_time, 
		   time_t *w_time, SMB_INO_T *ino)
{
	unsigned int data_len = 0;
	unsigned int param_len = 0;
	uint16 setup = TRANSACT2_QFILEINFO;
	pstring param;
	char *rparam=NULL, *rdata=NULL;

	/* if its a win95 server then fail this - win95 totally screws it
	   up */
	if (cli->win95) return False;

	param_len = 4;

	memset(param, 0, param_len);
	SSVAL(param, 0, fnum);
	SSVAL(param, 2, SMB_QUERY_FILE_ALL_INFO);

	if (!cli_send_trans(cli, SMBtrans2, 
                            NULL,                           /* name */
                            -1, 0,                          /* fid, flags */
                            &setup, 1, 0,                   /* setup, length, max */
                            param, param_len, 2,            /* param, length, max */
                            NULL, data_len, cli->max_xmit   /* data, length, max */
                           )) {
		return False;
	}

	if (!cli_receive_trans(cli, SMBtrans2,
                               &rparam, &param_len,
                               &rdata, &data_len)) {
		return False;
	}

	if (!rdata || data_len < 68) {
		return False;
	}

	if (c_time) {
		*c_time = convert_timespec_to_time_t(interpret_long_date(rdata+0));
	}
	if (a_time) {
		*a_time = convert_timespec_to_time_t(interpret_long_date(rdata+8));
	}
	if (m_time) {
		*m_time = convert_timespec_to_time_t(interpret_long_date(rdata+16));
	}
	if (w_time) {
		*w_time = convert_timespec_to_time_t(interpret_long_date(rdata+24));
	}
	if (mode) {
		*mode = SVAL(rdata, 32);
	}
	if (size) {
		*size = IVAL2_TO_SMB_BIG_UINT(rdata, 48);
	}
	if (ino) {
		*ino = IVAL(rdata, 64);
	}

	SAFE_FREE(rdata);
	SAFE_FREE(rparam);
	return True;
}

/***************************************************** 
return a connection to a server
*******************************************************/
int _System smbwrp_connect( Resource* pRes, cli_state ** cli)
{
    	smbwrp_server * srv = &pRes->srv;
	char * server = srv->server_name;
	char * share = *(srv->share_name) ? srv->share_name : "IPC$";
	char * workgroup = srv->workgroup;
	struct nmb_name called, calling;
	char *p, *server_n = server;
	fstring group;
	struct in_addr ip;
	NTSTATUS rc;
	struct cli_state * c;
	char* dev_type;

	zero_ip(&ip);

	debuglocal(1,"Connecting to \\\\%s:*********@%s:%s\\%s. Master %s:%d\n", srv->username,  workgroup, server, share, srv->master, srv->ifmastergroup);

	if (!*server) {
		struct in_addr sip;

		if (*workgroup)
		{
			if (!find_master_ip(workgroup, &sip)) {
				return 1;
			}
			fstrcpy(group, inet_ntoa(sip));
			server_n = group;
		} else
		if (*srv->master)
		{
			if (srv->ifmastergroup)
			{
				if (!find_master_ip(srv->master, &sip)) {
					return 11;
				}
				strncpy(srv->master, inet_ntoa(sip), sizeof(srv->master) - 1);
				srv->ifmastergroup = 0;
			}
			server_n = srv->master;
		} else
		{
			return 10;
		}		
	}

	make_nmb_name(&calling, global_myname(), 0x0);
//	make_nmb_name(&calling, "WORK", 0x0); // this machine name
	make_nmb_name(&called , server_n, 0x20);

 again:
        zero_ip(&ip);

	/* have to open a new connection */
	if (!(c=cli_initialise()))
	{
		return 2;
	}

	if (!NT_STATUS_IS_OK(cli_connect(c, server_n, &ip))) 
	{
		return 3;
	}

	if (pRes->krb5support == 1)
	{
	    debuglocal(1,"Kerberos support enabled\n");
	    c->use_kerberos = True;
	}

	if (!cli_session_request(c, &calling, &called)) {
		cli_shutdown(c);
		if (strcmp(called.name, "*SMBSERVER")) {
			make_nmb_name(&called , "*SMBSERVER", 0x20);
			goto again;
		}
		return 4;
	}

	debuglocal(4," session request ok\n");

	if (!cli_negprot(c)) {
		cli_shutdown(c);
		return 5;
	}

	debuglocal(4," session setuping for <%s>/<********> in <%s> %08x %08x %08x\n", srv->username,  workgroup, c->protocol, c->sec_mode, c->capabilities);

	if (!NT_STATUS_IS_OK(cli_session_setup(c, srv->username, 
			       srv->password, strlen(srv->password),
			       srv->password, strlen(srv->password),
			       workgroup))) {
		debuglocal(4,"%s/******** login failed\n", srv->username);
		/* try an anonymous login if it failed */
		if (!NT_STATUS_IS_OK(cli_session_setup(c, "", "", 1,"", 0, workgroup))) {
			debuglocal(4,"Anonymous login failed");
			cli_shutdown(c);
			return 6;
		}
	}

	debuglocal(4," session setup ok. Sending tconx <%s> <********>\n", share);

	// YD ticket:58 we need to check resource type to avoid connecting to printers.
	// dev type is set to IPC for IPC$, A: for everything else (printers use LPT1:)
	if (!strcmp( share, "IPC$"))
	    dev_type = "IPC";
	else
	    dev_type = "A:";

	if (!cli_send_tconX(c, share, dev_type,
			    srv->password, strlen(srv->password)+1)) {
		cli_shutdown(c);
		return 7;
	}

	debuglocal(4," tconx ok. cli caps %08x\n", c->capabilities);
	
	// save cli_state pointer
	*cli = c;

	return 0;
}

/***************************************************** 
close a connection to a server
*******************************************************/
void _System smbwrp_disconnect( Resource* pRes, cli_state * cli)
{
	if (pRes && cli)
	{
		// this call will free all buffers, close handles and free cli mem
		cli_shutdown( cli);
	}
}



/***************************************************** 
a wrapper for open()
*******************************************************/
int _System smbwrp_open(cli_state * cli, smbwrp_file * file)
{
	int fd = -1;

	if (!cli || !file || !*file->fname) 
	{
		return EINVAL;
	}
	if (file->denymode < DENY_ALL || file->denymode > DENY_NONE) 
	{
		file->denymode = DENY_NONE;
	}

	debuglocal(4,"cli_open(%s) attr %08x mode %02x denymode %02x\n", file->fname, file->openattr, file->openmode, file->denymode);
	file->fd = cli_open(cli, file->fname, file->openmode, file->denymode);
	if (file->fd == -1)
	{	
		return os2cli_errno(cli);
	}
	if (file->openmode & (O_WRONLY | O_RDWR | O_TRUNC | O_CREAT))
	{
		time_t t;
		file->mtime = time(NULL);
		t = get_time_zone(file->mtime);
		debuglocal(4,"cli_open mtime %lu %lu\n", file->mtime, t);
		file->mtime -= t;
	}
	file->offset = 0;
	return 0;
}

/***************************************************** 
a wrapper for read()
*******************************************************/
int _System smbwrp_read(cli_state * cli, smbwrp_file * file, void *buf, unsigned long count, unsigned long * result)
{
	int ret;

	if (!cli || !file || !buf || !result) 
	{
		return EINVAL;
	}
	
	*result = 0;
	ret = cli_read(cli, file->fd, buf, file->offset, count);
	if (ret == -1) 
	{
		return os2cli_errno(cli);
	}

	file->offset += ret;
	*result = ret;
	return 0;	
}

	

/***************************************************** 
a wrapper for write()
*******************************************************/
int _System smbwrp_write(cli_state * cli, smbwrp_file * file, void *buf, unsigned long count, unsigned long * result)
{
	int ret;

	if (!cli || !file || !buf || !result) 
	{
		return EINVAL;
	}
	
	*result = 0;
//debuglocal(1,("Write %x %d %lld %d", cli, file->fd, file->offset, count));
	ret = cli_write(cli, file->fd, 0, buf, file->offset, count);
	if (ret == -1) 
	{
		return os2cli_errno(cli);
	}

	file->offset += ret;
	*result = ret;
	return 0;	
}

/***************************************************** 
a wrapper for close()
*******************************************************/
int _System smbwrp_close(cli_state * cli, smbwrp_file * file)
{
	int rc = 0;
	if (!cli || !file) 
	{
		return EINVAL;
	}

	
	if (!cli_close(cli, file->fd))
	{
		return os2cli_errno(cli);
	}
	file->fd = -1;
	file->offset = 0;
	if (file->openattr || file->mtime)
	{
		debuglocal(4,"Set attr on close %s %08x %d %d\n", file->fname, file->openattr, file->mtime, file->mtime);
		if (!cli_setatr(cli, file->fname, file->openattr, file->mtime)) 
		{
			debuglocal(4,"Set attr on close failed %d\n", os2cli_errno(cli));
			//rc = os2cli_errno(cli);
		}	
		file->openattr = 0;
		file->mtime = 0;
	}
	*file->fname = 0;
	return rc;
}

/***************************************************** 
a wrapper for setfilesize()
*******************************************************/
int cli_setfilenewsize(struct cli_state *cli, int fnum, off_t newsize)
{
	unsigned int data_len = 8;
	unsigned int param_len = 6;
	uint16 setup = TRANSACT2_SETFILEINFO;
	pstring param;
	char *rparam=NULL, *rdata=NULL;

	memset(param, 0, param_len);
	SSVAL(param,0,fnum);
	SSVAL(param,2,SMB_SET_FILE_END_OF_FILE_INFO);

	if (!cli_send_trans(cli, SMBtrans2,
						NULL,                        /* name */
						-1, 0,                          /* fid, flags */
						&setup, 1, 0,                   /* setup, length, max */
						param, param_len, 2,            /* param, length, max */
						(char *)&newsize,  sizeof(newsize), cli->max_xmit /* data, length, max */
						)) {
		return False;
	}

	if (!cli_receive_trans(cli, SMBtrans2,
						&rparam, &param_len,
						&rdata, &data_len)) {
		return False;
	}

	SAFE_FREE(rdata);
	SAFE_FREE(rparam);

	return True;
}

int _System smbwrp_setfilesize(cli_state * cli, smbwrp_file * file, long long newsize)
{
	int rc = 0;
	if (!cli || !file) 
	{
		return EINVAL;
	}

	debuglocal(4,"cli_setnewfileszie(%s) %lld\n", file->fname, newsize);
	if (!cli_setfilenewsize(cli, file->fd, newsize))
	{
		if (newsize)
		{
			rc = os2cli_errno(cli);
		}

		if (!cli_close(cli, file->fd))
		{
			return os2cli_errno(cli);
		}
		file->fd = -1;
		file->offset = 0;			
		file->openmode &= ~(O_CREAT | O_EXCL);
		file->openmode |= O_TRUNC;
		debuglocal(4,"cli_setnewfileszie : cli_open(%s) attr %08x mode %02x denymode %02x\n", file->fname, file->openattr, file->openmode, file->denymode);
		file->fd = cli_open(cli, file->fname, file->openmode, file->denymode);
		if (file->fd == -1)
		{	
			return os2cli_errno(cli);
		}
	}	
	return 0;
}

/***************************************************** 
a wrapper for rename()
*******************************************************/
int _System smbwrp_rename(cli_state * cli, char *oldname, char *newname)
{
	if (!cli || !oldname || !newname) 
	{
		return EINVAL;
	}

	debuglocal(1,"Rename <%s> -> <%s>\n", oldname, newname);
	//cli_unlink(cli, newname);
//	if (!cli_rename(cli, oldname, newname) && !cli_ntrename(cli, oldname, newname)) 
	if (!cli_rename(cli, oldname, newname)) 
	{
		return os2cli_errno(cli);
	}
	return 0;
}


/***************************************************** 
a wrapper for chmod()
*******************************************************/
int _System smbwrp_setattr(cli_state * cli, smbwrp_fileinfo *finfo)
{
	if (!cli || !finfo || !*finfo->fname) 
	{
		return EINVAL;
	}

debuglocal(4,"Setting on <%s> attr %04x, time %lu/%lu\n", finfo->fname, finfo->attr, finfo->mtime, finfo->mtime + get_time_zone(finfo->mtime));
	if (!cli_setatr(cli, finfo->fname, finfo->attr, finfo->mtime + (finfo->mtime == 0 ? 0 : get_time_zone(finfo->mtime)))
		&& !cli_setatr(cli, finfo->fname, finfo->attr, 0)) 
	{
		return os2cli_errno(cli);
	}	
	return 0;
}

/***************************************************** 
a wrapper for unlink()
*******************************************************/
int _System smbwrp_unlink(cli_state * cli, const char *fname)
{
	if (!cli || !fname) 
	{
		return EINVAL;
	}
#if 0
	if (strncmp(cli->dev, "LPT", 3) == 0) 
	{
		int job = smbw_stat_printjob(cli, fname, NULL, NULL);
		if (job == -1) 
		{
			goto failed;
		}
		if (cli_printjob_del(cli, job) != 0) 
		{
			goto failed;
		}
	} else 
#endif
	if (!cli_unlink(cli, fname)) 
	{
		return os2cli_errno(cli);
	}
	return 0;
}

/***************************************************** 
a wrapper for lseek()
*******************************************************/
int _System smbwrp_lseek(cli_state * cli, smbwrp_file * file, int whence, long long offset)
{
	off_t size;
	if (!cli || !file) 
	{
		return EINVAL;
	}

	debuglocal(4,"lseek %d %lld %lld\n", whence, offset, file->offset);

	switch (whence) {
	case SEEK_SET:
		if (offset < 0)
		{
			return EINVAL;
		}
		file->offset = offset;
		break;
	case SEEK_CUR:
		file->offset += offset;
		break;
	case SEEK_END:
		if (offset > 0)
		{
			return EINVAL;
		}
		if (!cli_qfileinfo3(cli, file->fd, 
				   NULL, &size, NULL, NULL, NULL, 
				   NULL, NULL) &&
		    !cli_getattrE(cli, file->fd, 
				  NULL, (SMB_BIG_UINT *)&size, NULL, NULL, NULL)) 
		{
			return os2cli_errno(cli);
		}
		file->offset = size + offset;
		break;
	default: return EINVAL;
	}

	return 0;
}

/***************************************************** 
try to do a QPATHINFO and if that fails then do a getatr
this is needed because win95 sometimes refuses the qpathinfo
*******************************************************/
int _System smbwrp_getattr(smbwrp_server *srv, cli_state * cli, smbwrp_fileinfo *finfo)
{
	SMB_INO_T ino = 0;
	if (!cli || !finfo || !*finfo->fname) 
	{
		return EINVAL;
	}
	debug_printf( "smbwrp_getattr\n");
	debuglocal(4,"getattr %d %d <%s>\n", cli->capabilities & CAP_NOPATHINFO2, cli->capabilities & CAP_NT_SMBS, finfo->fname);
	if (!(cli->capabilities & CAP_NOPATHINFO2) &&
		cli_qpathinfo3(cli, finfo->fname, (time_t *)&finfo->ctime, (time_t *)&finfo->atime, (time_t *)&finfo->mtime, NULL,
			   (off_t *)&finfo->size, (unsigned short *)&finfo->attr, &ino))
	{
		finfo->attr &= 0x7F;
//debuglocal(2,("gotattr %08x <%s>\n", finfo->attr, finfo->fname));
//		finfo->ctime -= get_time_zone(finfo->ctime);
//		finfo->atime -= get_time_zone(finfo->atime);
//		finfo->mtime -= get_time_zone(finfo->mtime);
		return 0;
	}
//debuglocal(2,("getattr rc1 %d\n", os2cli_errno(cli)));

        /* If the path is not on a share (it is a workgroup or a server),
         * then cli_qpathinfo3 obviously fails. Return some fake information
         * about the directory.
         */
        if (   *srv->server_name == 0
            || (strcmp(cli->dev,"IPC") == 0)
            || *srv->share_name == 0 
            || (stricmp(srv->share_name,"IPC$") == 0)
            || (strncmp(cli->dev,"LPT",3) == 0) 
	   ) 
	{
            debuglocal(4,"getattr not a share.\n");
            *(time_t *)&finfo->ctime = time (NULL);
            *(time_t *)&finfo->atime = time (NULL);
            *(time_t *)&finfo->mtime = time (NULL);
            finfo->size = 0;
            finfo->easize = 0;
            finfo->attr = aDIR;
            return 0;
        }
	
	/* if this is NT then don't bother with the getatr */
	if (cli->capabilities & CAP_NT_SMBS && !(cli->capabilities & CAP_NOPATHINFO2)) 
	{
		int rc = os2cli_errno(cli);
		// cli_qpathinfo* reports EINVAL when path of given file not exists
		// thus there is no real situation when EINVAL should be returned to 
		// client at this point, we just replace it to ENOTDIR
		if (rc == EINVAL)
		{
			rc = ENOTDIR;
		}
		return rc;
	}

	if (cli_getatr(cli, finfo->fname, (unsigned short *)&finfo->attr, &finfo->size, (time_t *)&finfo->mtime)) 
	{
//debuglocal(2,("gotattr1 %08x <%s>\n", finfo->attr, finfo->fname));
		finfo->mtime -= get_time_zone(finfo->mtime);
		finfo->atime = finfo->atime;  //was mtime
		finfo->ctime = finfo->ctime;  //was mtime
		cli->capabilities &= CAP_NOPATHINFO2;
		return 0;
	}
	return os2cli_errno(cli);
}

/***************************************************** 
try to do a QPATHINFO and if that fails then do a getatr
this is needed because win95 sometimes refuses the qpathinfo
*******************************************************/
int _System smbwrp_fgetattr(cli_state * cli, smbwrp_file *file, smbwrp_fileinfo *finfo)
{
	SMB_INO_T ino = 0;
	if (!cli || !file || !finfo) 
	{
		return EINVAL;
	}

	strncpy(finfo->fname, file->fname, sizeof(finfo->fname) - 1);
	if (!cli_qfileinfo3(cli, file->fd, 
			   (unsigned short *)&finfo->attr, (off_t *)&finfo->size, (time_t *)&finfo->ctime, (time_t *)&finfo->atime, (time_t *)&finfo->mtime, NULL,
			   &ino))
	{
		if (!cli_getattrE(cli, file->fd, 
			  (unsigned short *)&finfo->attr, (SMB_BIG_UINT *)(&finfo->size), (time_t *)&finfo->ctime, (time_t *)&finfo->atime, (time_t *)&finfo->mtime)) 
		{
			return os2cli_errno(cli);
		}
		else
		{
			finfo->ctime -= get_time_zone(finfo->ctime);
			finfo->atime -= get_time_zone(finfo->atime);
			finfo->mtime -= get_time_zone(finfo->mtime);
		}
	}
	else
	{
//		finfo->ctime -= get_time_zone(finfo->ctime);
//		finfo->atime -= get_time_zone(finfo->atime);
//		finfo->mtime -= get_time_zone(finfo->mtime);
	}

	return 0;
}

// =============================DIRECTORY ROUTINES============================
                                                                                                                
/***************************************************** 
add a entry to a directory listing
*******************************************************/
static void smbwrp_dir_add(const char* mnt, smbwrp_fileinfo *finfo, const char *mask, void *state)
{
	if (state && finfo)
	{
		filelist_state * st  = (filelist_state *)state;
	    	char fullname[ _MAX_PATH];
	    	debuglocal(8,"adding <%s> %d %d\n", finfo->fname, sizeof(st->finfo), st->datalen);
		memcpy(&st->finfo, finfo, sizeof(st->finfo));
		StrCpy(fullname, st->dir);
		StrCat(fullname, finfo->fname);
		StrCpy(st->finfo.fname, fullname);
		getfindinfoL( st->pConn, st->plist, &st->finfo, st->ulAttribute, st->dir_mask);
	}
}

static void smbwrp_special_add(const char * name, void * state)
{
	smbwrp_fileinfo finfo = {0};

	if (!name)
	{
		return;
	}

	ZERO_STRUCT(finfo);

	strncpy(finfo.fname, name, sizeof(finfo.fname) - 1);
	finfo.attr = aRONLY | aDIR;

	smbwrp_dir_add("", &finfo, NULL, state);
}

static void smbwrp_printjob_add(struct print_job_info *job, void * state)
{
	smbwrp_fileinfo finfo = {0};

	ZERO_STRUCT(finfo);

//printf("Printjob <%s>\n", job->name);

	strncpy(finfo.fname, job->name, sizeof(finfo.fname) - 1);
	finfo.mtime = job->t - get_time_zone(job->t);
	finfo.atime = finfo.atime; //was mtime
	finfo.ctime = finfo.ctime; //was mtime
	finfo.attr = aRONLY;
	finfo.size = job->size;

	smbwrp_dir_add("", &finfo, NULL, state);
}

static void smbwrp_share_add(const char *share, uint32 type, 
			   const char *comment, void *state)
{
	smbwrp_fileinfo finfo = {0};

	// strip administrative names and printers from list
	if (type == STYPE_PRINTQ || strcmp(share,"IPC$") == 0) return;

	ZERO_STRUCT(finfo);

	strncpy(finfo.fname, share, sizeof(finfo.fname) - 1);
	finfo.attr = aRONLY | aDIR;	

	smbwrp_dir_add("", &finfo, NULL, state);
}

/****************************************************************************
 Interpret a long filename structure - this is mostly guesses at the moment.
 The length of the structure is returned
 The structure of a long filename depends on the info level. 260 is used
 by NT and 2 is used by OS/2
****************************************************************************/
// YD from libsmb\clilist.c
static size_t _os2_interpret_long_filename(struct cli_state *cli,
				   int level,char *p, smbwrp_fileinfo *finfo,
					uint32 *p_resume_key, DATA_BLOB *p_last_name_raw, uint32 *p_last_name_raw_len)
{
	extern file_info def_finfo;
	int len;
	char *base = p;
	smbwrp_fileinfo finfo1;
	
	if (!finfo) finfo = &finfo1;

	if (p_resume_key) {
		*p_resume_key = 0;
	}

	finfo->attr = def_finfo.mode;
	finfo->mtime = def_finfo.mtime_ts.tv_sec;
	finfo->atime = def_finfo.atime_ts.tv_sec;
	finfo->ctime = def_finfo.ctime_ts.tv_sec;
	strncpy(finfo->fname, def_finfo.name, sizeof(finfo->fname) - 1);
	debug_printf( "fname %s (serverzone %d, level %d)\n",finfo->fname, cli->serverzone, level);
	
	switch (level) {
		case 1: /* OS/2 understands this */
			/* these dates are converted to GMT by
                           make_unix_date */
			finfo->ctime = cli_make_unix_date2(cli, p+4) - cli->serverzone;
			finfo->atime = cli_make_unix_date2(cli, p+8) - cli->serverzone;
			finfo->mtime = cli_make_unix_date2(cli, p+12) - cli->serverzone;
			finfo->size = IVAL(p,16);
			finfo->attr = CVAL(p,24);
			len = CVAL(p, 26);
			p += 27;
			p += clistr_align_in(cli, p, 0);
			/* the len+2 below looks strange but it is
			   important to cope with the differences
			   between win2000 and win9x for this call
			   (tridge) */
			p += clistr_pull(cli, finfo->fname, p,
					 sizeof(finfo->fname),
					 len+2,
					 STR_TERMINATE);
			finfo->easize = -1;
			return PTR_DIFF(p, base);

		case 2: /* this is what OS/2 uses mostly */
			/* these dates are converted to GMT by
                           make_unix_date */
			finfo->ctime = cli_make_unix_date2(cli, p+4) - cli->serverzone;
			finfo->atime = cli_make_unix_date2(cli, p+8) - cli->serverzone;
			finfo->mtime = cli_make_unix_date2(cli, p+12) - cli->serverzone;
			finfo->size = IVAL(p,16);
			finfo->attr = CVAL(p,24);
			finfo->easize = IVAL(p,26);
			len = CVAL(p, 30);
			p += 31;
			/* check for unisys! */
			p += clistr_pull(cli, finfo->fname, p,
					 sizeof(finfo->fname),
					 len, 
					 STR_NOALIGN);
			return PTR_DIFF(p, base) + 1;
			
		case 260: /* NT uses this, but also accepts 2 */
		{
			size_t namelen, slen;
			p += 4; /* next entry offset */
			if (p_resume_key) {
				*p_resume_key = IVAL(p,0);
			}

			p += 4; /* fileindex */
				
			/* Offset zero is "create time", not "change time". */
			p += 8;
			finfo->atime = interpret_long_date(p).tv_sec;
			p += 8;
			finfo->mtime = interpret_long_date(p).tv_sec;
			p += 8;
			finfo->ctime = interpret_long_date(p).tv_sec;
			p += 8;
			finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
			p += 8;
			p += 8; /* alloc size */
			finfo->attr = CVAL(p,0);
			p += 4;
			namelen = IVAL(p,0);
			p += 4;
			finfo->easize = IVAL(p,0);
			p += 4; /* EA size */
			slen = SVAL(p, 0);
			p += 2; 
#if 0			
			{
				/* stupid NT bugs. grr */
				int flags = 0;
				if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
				clistr_pull(cli, finfo->short_name, p,
					    sizeof(finfo->short_name),
					    slen, flags);
			}
#endif
			p += 24; /* short name? */	  
			clistr_pull(cli, finfo->fname, p,
				    sizeof(finfo->fname),
				    namelen, 0);

			/* To be robust in the face of unicode conversion failures
			   we need to copy the raw bytes of the last name seen here.
			   Namelen doesn't include the terminating unicode null, so
			   copy it here. */
#if 0
			if (p_last_name_raw && p_last_name_raw_len) {
				if (namelen + 2 > p_last_name_raw->length) {
					memset(p_last_name_raw->data, '\0', sizeof(p_last_name_raw->length));
					*p_last_name_raw_len = 0;
				} else {
					memcpy(p_last_name_raw->data, p, namelen);
					SSVAL(p_last_name_raw->data, namelen, 0);
					*p_last_name_raw_len = namelen + 2;
				}
			}
#endif
			return (size_t)IVAL(base, 0);
		}
	}
	
	debuglocal(1,"Unknown long filename format %d\n",level);
	return (size_t)IVAL(base,0);
}

/****************************************************************************
 Do a directory listing, calling fn on each file found.
 Modified from cli_list_new
****************************************************************************/

static int list_files(struct cli_state *cli, const char *Mask, uint16 attribute, 
		 void (*fn)(const char*, smbwrp_fileinfo *, const char *, void *), void *state)
{
#if 1
	int max_matches = 1366; /* Match W2k - was 512. */
#else
	int max_matches = 512;
#endif
	int info_level;
	char *p, *p2;
	pstring mask;
	smbwrp_fileinfo finfo;
	int i;
	char *dirlist = NULL;
	int dirlist_len = 0;
	int total_received = -1;
	BOOL First = True;
	int ff_searchcount=0;
	int ff_eos=0;
	int ff_dir_handle=0;
	int loop_count = 0;
	char *rparam=NULL, *rdata=NULL;
	unsigned int param_len, data_len;	
	uint16 setup;
	pstring param;
	const char *mnt;
	uint32 resume_key = 0;
	uint32 last_name_raw_len = 0;
	DATA_BLOB last_name_raw = data_blob(NULL, 2*sizeof(pstring));

	/* NT uses 260, OS/2 uses 2. Both accept 1. */
	info_level = (cli->capabilities&CAP_NT_SMBS)?260:2; 

	debuglocal(4,"list_files level %d. mask <%s>\n", info_level, mask);

	/* when getting a directory listing from a 2k dfs root share, 
	   we have to include the full path (\server\share\mask) here */
	   
	if ( cli->dfsroot )
		pstr_sprintf( mask, "\\%s\\%s\\%s", cli->desthost, cli->share, Mask );
	else
		pstrcpy(mask,Mask);

	while (ff_eos == 0) {
		loop_count++;
		if (loop_count > 200) {
			debuglocal(0,"Error: Looping in FIND_NEXT??\n");
			break;
		}

		if (First) {
			setup = TRANSACT2_FINDFIRST;
			SSVAL(param,0,attribute); /* attribute */
			SSVAL(param,2,max_matches); /* max count */
			SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));	/* resume required + close on end */
			SSVAL(param,6,info_level); 
			SIVAL(param,8,0);
			p = param+12;
			p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
					 STR_TERMINATE);
		} else {
			setup = TRANSACT2_FINDNEXT;
			SSVAL(param,0,ff_dir_handle);
			SSVAL(param,2,max_matches); /* max count */
			SSVAL(param,4,info_level); 
			/* For W2K servers serving out FAT filesystems we *must* set the
			   resume key. If it's not FAT then it's returned as zero. */
			SIVAL(param,6,resume_key); /* ff_resume_key */
			/* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
			   can miss filenames. Use last filename continue instead. JRA */
			SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));	/* resume required + close on end */
			p = param+12;
			if (last_name_raw_len && (last_name_raw_len < (sizeof(param)-12))) {
				memcpy(p, last_name_raw.data, last_name_raw_len);
				p += last_name_raw_len;
			} else {
				p += clistr_push(cli, param+12, mask, sizeof(param)-12, STR_TERMINATE);
			}
		}

		param_len = PTR_DIFF(p, param);

		if (!cli_send_trans(cli, SMBtrans2, 
				    NULL,                   /* Name */
				    -1, 0,                  /* fid, flags */
				    &setup, 1, 0,           /* setup, length, max */
				    param, param_len, 10,   /* param, length, max */
				    NULL, 0, 
#if 0
				    /* w2k value. */
				    MIN(16384,cli->max_xmit) /* data, length, max. */
#else
				    cli->max_xmit	    /* data, length, max. */
#endif
				    )) {
			break;
		}

		if (!cli_receive_trans(cli, SMBtrans2, 
				       &rparam, &param_len,
				       &rdata, &data_len) &&
                    cli_is_dos_error(cli)) {
			/* we need to work around a Win95 bug - sometimes
			   it gives ERRSRV/ERRerror temprarily */
			uint8 eclass;
			uint32 ecode;

			SAFE_FREE(rdata);
			SAFE_FREE(rparam);

			cli_dos_error(cli, &eclass, &ecode);

			/*
			 * OS/2 might return "no more files",
			 * which just tells us, that searchcount is zero
			 * in this search.
			 * Guenter Kukkukk <linux@kukkukk.com>
			 */

			if (eclass == ERRDOS && ecode == ERRnofiles) {
				ff_searchcount = 0;
				cli_reset_error(cli);
				break;
			}

			if (eclass != ERRSRV || ecode != ERRerror)
				break;
			smb_msleep(100);
			continue;
		}

                if (cli_is_error(cli) || !rdata || !rparam) 
		{
			if (First && info_level == 2)
			{
				// we have tried query ea size, but now will try without ea size
				info_level = 1;
				debuglocal(4,"list_files fallback to level %d\n", info_level);
				continue;
			}
			SAFE_FREE(rdata);
			SAFE_FREE(rparam);
			break;
		}

		if (total_received == -1)
			total_received = 0;

		/* parse out some important return info */
		p = rparam;
		if (First) {
			ff_dir_handle = SVAL(p,0);
			ff_searchcount = SVAL(p,2);
			ff_eos = SVAL(p,4);
		} else {
			ff_searchcount = SVAL(p,0);
			ff_eos = SVAL(p,2);
		}
		debuglocal(4,"list_files %d %d %d %d\n", ff_searchcount, ff_eos, "(ff_lastname)", First);

		if (ff_searchcount == 0) {
			SAFE_FREE(rdata);
			SAFE_FREE(rparam);
			break;
		}

		/* point to the data bytes */
		p = rdata;

		memset(&finfo, 0, sizeof(finfo));
		finfo.easize = -1;
		/* we might need the lastname for continuations */
		for (p2=p,i=0;i<ff_searchcount;i++) {
			if ((info_level == 260) && (i == ff_searchcount-1)) {
				/* Last entry - fixup the last offset length. */
				SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
			}
			p2 += _os2_interpret_long_filename(cli,info_level,p2,&finfo,
							&resume_key,&last_name_raw,&last_name_raw_len);

			if (!First && *mask && strcsequal(finfo.fname, mask)) {
				debuglocal(0,"Error: Looping in FIND_NEXT as name %s has already been seen?\n",
					finfo.fname);
				ff_eos = 1;
				break;
			}
		}

		if (ff_searchcount > 0) {
			pstrcpy(mask, finfo.fname);
		} else {
			pstrcpy(mask,"");
		}

		/* grab the data for later use */
		/* and add them to the dirlist pool */
		dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);

		if (!dirlist) {
			debuglocal(0,"cli_list_new: Failed to expand dirlist\n");
			SAFE_FREE(rdata);
			SAFE_FREE(rparam);
			break;
		}

		memcpy(dirlist+dirlist_len,p,data_len);
		dirlist_len += data_len;

		total_received += ff_searchcount;

		SAFE_FREE(rdata);
		SAFE_FREE(rparam);

		debuglocal(3,"received %d entries (eos=%d)\n",
			 ff_searchcount,ff_eos);

		if (ff_searchcount > 0)
			loop_count = 0;

		First = False;
	}

	mnt = cli_cm_get_mntpoint( cli );

        /* see if the server disconnected or the connection otherwise failed */
        if (cli_is_error(cli)) {
                total_received = -1;
        } else {
                /* no connection problem.  let user function add each entry */
                for (p=dirlist,i=0;i<total_received;i++) {
                        p += _os2_interpret_long_filename(cli, info_level, p,
                                                     &finfo,NULL,NULL,NULL);
                        fn( mnt,&finfo, Mask, state );
                }
        }

	/* free up the dirlist buffer and last name raw blob */
	SAFE_FREE(dirlist);
	data_blob_free(&last_name_raw);
	return(total_received);
}


/***************************************************** 
open a directory on the server
*******************************************************/
int _System smbwrp_filelist(smbwrp_server *srv, cli_state * cli, filelist_state * state)
{
	if (!srv || !cli || !state || !*state->mask)
	{
		return EINVAL;
	}
	debuglocal(1,"Filelist <%s> on master <%s> wgrp <%s> server <%s> share <%s> clidev <%s>\n", state->mask, srv->master, srv->workgroup, srv->server_name, srv->share_name, cli->dev);
	debug_printf( "Filelist <%s> on master <%s> wgrp <%s> server <%s> share <%s> clidev <%s>\n", state->mask, srv->master, srv->workgroup, srv->server_name, srv->share_name, cli->dev);
	if (*srv->workgroup == 0 && *srv->server_name == 0) 
	{
		smbwrp_special_add(".", state);
		smbwrp_special_add("..", state);
		cli_NetServerEnum(cli, srv->master, SV_TYPE_DOMAIN_ENUM,
				   smbwrp_share_add, state);
	} else 
	if (*srv->server_name == 0) 
	{
		smbwrp_special_add(".", state);
		smbwrp_special_add("..", state);

		cli_NetServerEnum(cli, srv->workgroup, SV_TYPE_ALL,
				   smbwrp_share_add, state);
	} else 
	if ((strcmp(cli->dev,"IPC") == 0) || *srv->share_name == 0 || (stricmp(srv->share_name,"IPC$") == 0)) 
	{
		smbwrp_special_add(".", state);
		smbwrp_special_add("..", state);

                if (net_share_enum_rpc(cli, smbwrp_share_add, state) < 0 &&
                            cli_RNetShareEnum(cli,smbwrp_share_add, state) < 0) 
		{
			return os2cli_errno(cli);
		}
	} else 
	if (strncmp(cli->dev,"LPT",3) == 0) 
	{
		smbwrp_special_add(".", state);
		smbwrp_special_add("..", state);
		if (cli_print_queue_state(cli, smbwrp_printjob_add, state) < 0) 
		{
			return os2cli_errno(cli);
		}
	} 
	else 
	{
#if 0
		if (strcmp(path,"\\") == 0) {
			smbwrp_special_add(".", state);
			smbwrp_special_add("..", state);
		}
#endif
#if 0
		if (cli_list(cli, state->mask, aHIDDEN|aSYSTEM|aDIR, 
			     smbwrp_dir_add_old, state) < 0) 
#else
		if (list_files(cli, state->mask, aHIDDEN|aSYSTEM|aDIR, 
			     smbwrp_dir_add, state) < 0) 
#endif
		{                                                                              
			return os2cli_errno(cli);
		}
	}                                                                                                                                                

	return 0;
}

/***************************************************** 
a wrapper for chdir()
*******************************************************/
int _System smbwrp_chdir(smbwrp_server *srv, cli_state * cli, char *fname)
{
	unsigned short mode = aDIR;
	smbwrp_fileinfo finfo = {0};
	if (!cli || !fname) 
	{
		return EINVAL;
	}

	strncpy(finfo.fname, fname, sizeof(finfo.fname) - 1);
	if (smbwrp_getattr(srv, cli, &finfo)) 
	{
		return os2cli_errno(cli);
	}

	if (!(finfo.attr & aDIR)) {
		return ENOTDIR;
	}

	return 0;
}


/***************************************************** 
a wrapper for mkdir()
*******************************************************/
int _System smbwrp_mkdir(cli_state * cli, char *fname)
{
	if (!cli || !fname) 
	{
		return EINVAL;
	}

	if (!cli_mkdir(cli, fname)) 
	{
		return os2cli_errno(cli);
	}
	return 0;
}

/***************************************************** 
a wrapper for rmdir()
*******************************************************/
int _System smbwrp_rmdir(cli_state * cli, char *fname)
{
	if (!cli || !fname) 
	{
		return EINVAL;
	}

	if (!cli_rmdir(cli, fname)) 
	{
		return os2cli_errno(cli);
	}
	return 0;
}

/***************************************************** 
set EA for a path
*******************************************************/
int _System smbwrp_setea(cli_state * cli, char *fname, char * name, unsigned char * value, int size)
{
	if (!cli || !fname || !name) 
	{
		return EINVAL;
	}
	if (!cli_set_ea_path(cli, fname, name, value, size)) 
	{
		return os2cli_errno(cli);
	}
	return 0;
}

/***************************************************** 
set EA for a file
*******************************************************/
int _System smbwrp_fsetea(cli_state * cli, smbwrp_file *file, char * name, unsigned char * value, int size)
{
	if (!cli || !file || !name) 
	{
		return EINVAL;
	}
	if (!cli_set_ea_fnum(cli, file->fd, name, value, size)) 
	{
		return os2cli_errno(cli);
	}
	return 0;
}


#pragma pack(1)
typedef struct _FEA         /* fea */
{
         unsigned char fEA;           /* flags                              */
         unsigned char cbName;        /* name length not including NULL */
         unsigned short cbValue;     /* value length */
} FEA;

typedef struct _FEALIST     /* feal */
{
	unsigned long cbList;       /* total bytes of structure including full list */
	FEA list[1];        /* variable length FEA structures */
} FEALIST;
#pragma pack()

static int unilistea(cli_state * cli, char *fname, smbwrp_file *file, void * buffer, unsigned long size)
{
	int fnum, i;
	int gotsize = sizeof(unsigned long);
	size_t num_eas;
	struct ea_struct *ea_list = NULL;
	TALLOC_CTX *mem_ctx;
	FEA * p;
	FEALIST * pfealist;
	char * q;

	mem_ctx = talloc_init("%d: ealist", _gettid());
	pfealist = (FEALIST *)buffer;
	pfealist->cbList = 0;

	if (file)
	{
		if (!cli_get_ea_list_fnum(cli, file->fd, mem_ctx, &num_eas, &ea_list)) 
		{
			debuglocal(4,"ea_get_fnum list failed - %s\n", cli_errstr(cli));
			talloc_destroy(mem_ctx);
			return os2cli_errno(cli);
		}
	}
	else
	{
		if (!cli_get_ea_list_path(cli, fname, mem_ctx, &num_eas, &ea_list)) 
		{
			debuglocal(4,"ea_get_file list failed - %s\n", cli_errstr(cli));
			talloc_destroy(mem_ctx);
			return os2cli_errno(cli);
		}
	}

	debuglocal(4,"num_eas = %d\n", num_eas);

	// we will count that os/2 max EA size for file is 64kb
	p = pfealist->list;
	for (i = 0; i < num_eas; i++) 
	{
		int namelen = strlen(ea_list[i].name);
		debuglocal(4, "%d Got EA <%s> with namelen %d, size %d. Gross %d. Buf %d\n", i, ea_list[i].name, namelen, ea_list[i].value.length, gotsize, size);
		if (namelen > 0xFF || ea_list[i].value.length > 0xFFFF)
		{
			debuglocal(4, "Skip EA <%s> with namelen %d, size %d\n", ea_list[i].name, namelen, ea_list[i].value.length);
			continue;
		}
		gotsize += sizeof(FEA) + namelen + ea_list[i].value.length + 1;
		if (size >= gotsize)
		{
			p->fEA = 0;
			p->cbName = namelen;
			p->cbValue = ea_list[i].value.length;
			q = (char *)(p + 1);
			strncpy(q, ea_list[i].name, namelen + 1);
			q += namelen + 1;
			memcpy(q, ea_list[i].value.data, ea_list[i].value.length);
			p = (FEA *)(q + ea_list[i].value.length);
		}
	}
	pfealist->cbList = gotsize;
	debuglocal(4,"ret size = %d\n", gotsize);

	talloc_destroy(mem_ctx);
	return 0;
}

/***************************************************** 
lists EA of a path
*******************************************************/
int _System smbwrp_listea(cli_state * cli, char *fname, void * buffer, unsigned long size)
{
	if (!cli || !fname || !buffer)
	{
		return EINVAL;
	}

	debuglocal(4,"EALIst for <%s>\n", fname);
	return unilistea(cli, fname, NULL, buffer, size);
}

/***************************************************** 
lists EA of a file
*******************************************************/
int _System smbwrp_flistea(cli_state * cli, smbwrp_file *file, void * buffer, unsigned long size)
{
	if (!cli || !file || !buffer)
	{
		return EINVAL;
	}

	debuglocal(4,"FEALIst for <%s>/%d\n", file->fname, file->fd);
	return unilistea(cli, NULL, file, buffer, size);
}

/****************************************************************************
Check the space on a device.
****************************************************************************/
int _System smbwrp_dskattr(cli_state * cli, FSALLOCATE *pfsa)
{
	int total, bsize, avail;

	if (!cli || !pfsa)
	{
		return EINVAL;
	}

	if (!cli_dskattr(cli, &bsize, &total, &avail)) 
	{
		debuglocal(4,"Error in dskattr: %s\n",cli_errstr(cli));
		return os2cli_errno(cli);
	}

	debuglocal(4,"\n\t\t%d blocks of size %d. %d blocks available\n",
		 total, bsize, avail);

	// YD currently Samba return it in MB!
	pfsa->cSectorUnit = 1;
	if (bsize > 65536) 
	{
		pfsa->cUnit = total*1024;
		pfsa->cUnitAvail = avail*1024;
		pfsa->cbSector = bsize/1024;
	} 
	else 
	{
		pfsa->cUnit = total;
		pfsa->cUnitAvail = avail;
		pfsa->cbSector = bsize;
	}

	return 0;
}

