
/*
    LinuxWare daemon - Netware like server for Linux

    Copyright (C) 1994, 1995  Ales Dryak <e-mail: A.Dryak@sh.cvut.cz>

    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 <string.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include "ncp.h"
#include "logging.h"
#include "filesearch.h"
#include "handlers.h"
#include "dirops.h"

static void allocate_dir_handle(char* data,connection* c,int mode)
{
	struct ncp_alloc_dir_rq* dtrq=
		(struct ncp_alloc_dir_rq*)data;
	struct ncp_alloc_dir_rpl* rpl=
		(struct ncp_alloc_dir_rpl*)get_conn_buf(c);
	char* path;	
	vol_info* vi;
	dir_handle* sdh;
	
	if ((sdh=get_dir_handle(c,dtrq->src_handle))!=NULL)
	{
		char upath[MAX_PATH];
		struct stat fs;
		
		dtrq->name[dtrq->name_len]=0;
		if (!(nw2unix_path(upath,sdh,dtrq->name)!=NULL && 
		      (stat(upath,&fs)==0) && (fs.st_mode&S_IFDIR)))
		{
			ncp_send_failure(c,COMPL_INVALID_PATH);
			return;
		}
		for(path=dtrq->name;*path;path++)
		{
			if (*path==':') break;
		}
		if (*path==0)
		{
			path=dtrq->name;
			vi=sdh->volume;
		}
		else
		{
			*path++=0;
			vi=find_volume(dtrq->name);
		}
		if (vi!=NULL)
		{
			int dir_idx;
			dir_idx=alloc_dir(c,vi,path,mode);
			if (dir_idx>=0)
			{
				rpl->handle=dir_idx;
				rpl->access_rights=unix2nw_rights(fs.st_mode,fs.st_uid,fs.st_gid); /* 0 hard wired */
				ncp_send(c,sizeof(*rpl));
				return;
			}
			else
			{
				ncp_send_failure(c,COMPL_FAILURE);
				return;
			}
		}
	}
	ncp_send_failure(c,COMPL_CANT_DISK_MAP);
	return;
}

void ncp_handle_r22(int subfunc,int size,char* data,connection* c)
{
	switch(subfunc)
	{
	case 0: /* set directory handle */
	{
		struct ncp_set_dir_handle* rq=(struct ncp_set_dir_handle*)data;
		dir_handle* dh;
		dir_handle* sdh;
		
		if ((dh=get_dir_handle(c,rq->dest_handle))!=NULL &&
		    (sdh=get_dir_handle(c,rq->src_handle))!=NULL &&
		    (dh->volume==sdh->volume))
		{
			char* p;
			char* q;

			
			(q=p=rq->name)[rq->name_len]=0;
			for(;*p;p++)
			{
				*p&=0x7F;
				if (*p=='\\') *p='/';
			}
			if (*q=='/')
			{
				strncpy(dh->path,q+1,sizeof(dh->path)-1);
				dh->path[sizeof(dh->path)-1]=0;
			}
			else
			{
				strcpy(dh->path,sdh->path);
				if (*q!=0 && strcmp(q,".")!=0)
				{
					if (strcmp(q,"..")!=0)
					{
					
						if (dh->path[0]!=0 && dh->path[strlen(dh->path)-1]!='/')
						{
							strncat(dh->path,"/",sizeof(dh->path)-strlen(dh->path)-1);
							dh->path[sizeof(dh->path)-1]=0;
						}
						strncat(dh->path,q,sizeof(dh->path)-strlen(dh->path)-1);
						dh->path[sizeof(dh->path)-1]=0;
					}
					else
					{
						if ((p=strrchr(dh->path,'/'))!=NULL)
						{
							*p=0;
						}
						else
						{
							dh->path[0]=0;
						}
					}
				}
			}
			ncp_send_ok(c);
		}
		else
		{
			ncp_send_failure(c,COMPL_FAILURE);
		}
		return;
	}
	case 1: /* get dir path */
	{
		byte* local_dh=(byte*)data;
		struct ncp_string* rpl=(struct ncp_string*)get_conn_buf(c);
		dir_handle* dh;
		
		if ((dh=get_dir_handle(c,*local_dh))!=NULL)
		{
			unix2nw_path(rpl->name,dh->volume,dh->path);
			rpl->name_len=strlen(rpl->name);
			LPRINTF(LL_DEBUG,("get path: %s\n",rpl->name));
			ncp_send(c,1+rpl->name_len);
		}
		else
		{
			ncp_send_failure(c,COMPL_FAILURE);
		}
		return;
	}
	case 2: /* scan dir info scan dirs*/
	{
		struct ncp_scandir_dir_rq* rq=(struct ncp_scandir_dir_rq*)data;
		struct ncp_scandir_dir_rpl* rpl=(struct ncp_scandir_dir_rpl*)get_conn_buf(c);
		dir_handle* dh;
		 		
		if ((dh=get_dir_handle(c,rq->dir_handle))!=NULL)
		{
			ncp_filedir_prop prop;

			rq->path.name[rq->path.name_len]=0;
			if (nw_file_search1((ncp_filename*)rpl->path,&(rpl->context),&prop,
			    dh,rq->path.name,0x10,htons(ntohs(rq->context)-1)))
			{				/* 0x10 = hard wired dirs */
				rpl->d_creat=prop.dir.d_creat;
				rpl->t_creat=prop.dir.t_creat;
				rpl->uid=prop.dir.uid;
				rpl->access=prop.dir.d_acc;
				rpl->zero=0;
				ncp_send(c,sizeof(*rpl));
				return;
			}
		}
		ncp_send_failure(c,COMPL_FAILURE);
		return;
	}
	case 42: /* get my effective rights for dir entry */
	case 3: /* get effective dir rights */
	{
		struct ncp_get_eff_rights* rq=(struct ncp_get_eff_rights*)data;
		byte* rights=(byte*)get_conn_buf(c);
		dir_handle* dh;
		
		if ((dh=get_dir_handle(c,rq->dir_handle))!=NULL)
		{
			char path[MAX_PATH];
			struct stat fs;
			
			rq->path[rq->path_len]=0;
			if (nw2unix_path(path,dh,rq->path)!=NULL && 
			    (stat(path,&fs)==0) && (subfunc==42 || (fs.st_mode&S_IFDIR)))
			{
				*rights=unix2nw_rights(fs.st_mode,fs.st_uid,fs.st_gid); /* 0 hard wired */
				ncp_send(c,sizeof(*rights));
				return;
			}
			ncp_send_failure(c,COMPL_INVALID_PATH);
			return;
		}
		ncp_send_failure(c,COMPL_FAILURE);
		return;
	}
	case 5: /* get volume number */
	{
		struct ncp_string* vol_name=(struct ncp_string*)data;
		byte* vid=(byte*)get_conn_buf(c);
		vol_info* vi;
		
		vol_name->name[vol_name->name_len]=0;
		vi=find_volume(vol_name->name);
		if (vi!=NULL)
		{
			int id=get_volume_id(vi);
			
			if (id>=0)
			{
				*vid=id;
				ncp_send(c,sizeof(*vid));
				return;
			}	
		}
		ncp_send_failure(c,COMPL_FAILURE);
		return;
	}
	case 6: /* get volume name */
	{
		byte vid=*(byte*)data;
		struct ncp_string* rpl=(struct ncp_string*)get_conn_buf(c);
		
		if (vid<MAX_VOLUME)
		{
			rpl->name_len=strlen(get_volume(vid)->name);
			strcpy(rpl->name,get_volume(vid)->name);
			ncp_send(c,sizeof(rpl->name_len)+rpl->name_len);
			return;
		}
		ncp_send_failure(c,COMPL_FAILURE);
		return;
	}
	case 10: /* create directory */
	{
		struct ncp_create_dir* rq=(struct ncp_create_dir*)data;
		dir_handle* dh;
		
		if ((dh=get_dir_handle(c,rq->dir_handle))!=NULL)
		{
			char path[MAX_PATH];
			rq->path.name[rq->path.name_len]=0;
			nw2unix_path(path,dh,rq->path.name);
			if (mkdir(path,0777)!=-1) /* hard wired */
			{
				ncp_send_ok(c);
				return;
			}
		}
		ncp_send_failure(c,COMPL_FAILURE);
		return;		
	}
	case 11: /* delete directory */
	{
		struct ncp_delete_dir* rq=(struct ncp_delete_dir*)data;
		dir_handle* dh;
		
		if ((dh=get_dir_handle(c,rq->dir_handle))!=NULL)
		{
			char path[MAX_PATH];
			rq->path.name[rq->path.name_len]=0;
			nw2unix_path(path,dh,rq->path.name);
			if (rmdir(path)!=-1)
			{
				ncp_send_ok(c);
				return;
			}
			if (errno==ENOTEMPTY)
			{
				ncp_send_failure(c,COMPL_DIR_NOT_EMPTY);
				return;
			}
		}
		ncp_send_failure(c,COMPL_FAILURE);
		return;		
	}
	case 18: /* alloc permanent dir handle */
	{
		allocate_dir_handle(data,c,DH_PERMANENT);
		return;
	}
	case 19: /* alloc temp dir handle */
	{
		allocate_dir_handle(data,c,DH_TEMPORARY);
		return;
	}
	case 20: /* dealloc dir handle */
	{
		byte* local_dh=(byte*)data;
		
		if (get_dir_handle(c,*local_dh)!=NULL)
		{
			free_dir(c,*local_dh);
			ncp_send_ok(c);
		}
		else
		{
			ncp_send_failure(c,COMPL_FAILURE);
		}
		return;
	}
	case 21: /* get volume info with handle */
	{
		byte* local_dh=(byte*)data;
		struct ncp_get_volinfo* rpl=(struct ncp_get_volinfo*)get_conn_buf(c);
		dir_handle* dh;
		
		if ((dh=get_dir_handle(c,*local_dh))!=NULL)
		{
			char path[MAX_PATH];
			
			if (nw2unix_path(path,dh,"")!=NULL)
			{
				struct statfs st;
				int scale;
				
				statfs(path,&st);
				for(scale=1;st.f_blocks/scale>0xFFFF;scale*=2) {}
				rpl->sec_per_cluster=htons(scale*st.f_bsize/512);
				rpl->total_clusters=htons(st.f_blocks/scale);
				rpl->avail_clusters=htons(st.f_bavail/scale);
				rpl->dir_slots=htons(st.f_files);
				rpl->avail_dir_slots=htons(st.f_ffree);
				memcpy(rpl->name,dh->volume->name,sizeof(rpl->name));
				rpl->removable=htons(0);
				ncp_send(c,sizeof(*rpl));
				return;
			}
		}
		ncp_send_failure(c,COMPL_FAILURE);
		return;
	}
	case 32: /* scan volume's user disk limitation */
	{
		/* dummy */
		byte* rpl=(byte*)get_conn_buf(c);

		*rpl=0; /* no restrictions, hard wired */
		ncp_send(c,sizeof(*rpl));		
		return;
	}
	default:LPRINTF(LL_FULL,("Unknown function (22,%d)\n",subfunc));
		ncp_send_failure(c,COMPL_FAILURE);
		return;	
	}
}

#define NMSP_DOS 0
#define HF_NOHANDLE 255

struct ncp_string* ncpscat(char* s,struct ncp_string* name)
{
	int new_size=strlen(s)+name->name_len;
	memcpy(s+strlen(s),name->name,name->name_len);
	s[new_size]=0;
	return (struct ncp_string*)(((char*)name)+name->name_len+1);
}

void ncp_handle_r87(int subfunc,int size,char* data,connection* c)
{
	switch(subfunc)
	{
	case 12: /* alloc short directory handle */
	{
		struct ncp_alloc_short_dir_rq* rq=(struct ncp_alloc_short_dir_rq*)data;
		struct ncp_alloc_short_dir_rpl* rpl=(struct ncp_alloc_short_dir_rpl*)get_conn_buf(c);
		
		if (rq->name_space==NMSP_DOS && /* hard wired cond */
		    rq->handle_flag==HF_NOHANDLE )
		{
			vol_name vname;
			vol_info* vi;
			struct ncp_string* nn;
			
			*vname=0;
			nn=ncpscat(vname,&(rq->path));
			if ((vi=find_volume(vname))!=NULL)
			{
				struct stat fs;
				int comp;
				char nwpath[MAX_PATH];
				char path[MAX_PATH];
				char* dir_path;
				
				strcpy(nwpath,vname);
				strcat(nwpath,":");
				dir_path=nwpath+strlen(nwpath);
				for(comp=rq->path_comp-1;comp--;)
				{
					strcat(nwpath,"/");
					nn=ncpscat(nwpath,nn);
				}				
				
				if ( (nw2unix_path(path,get_dir_handle(c,ROOT_DIR_ID),nwpath)!=NULL) &&
				     (stat(path,&fs)==0) && (fs.st_mode&S_IFDIR) )
				{
					int dir_idx=alloc_dir(c,vi,dir_path,ntohs(rq->mode)&1?DH_TEMPORARY:DH_PERMANENT);
					if (dir_idx>=0)
					{
						rpl->handle=dir_idx;
						rpl->vol_id=get_volume_id(vi);
						ncp_send(c,sizeof(*rpl));
						return;
					}
				}
			}
		}
		ncp_send_failure(c,COMPL_FAILURE);
		return;
	}
	default:LPRINTF(LL_FULL,("Unknown function (87,%d)\n",subfunc));
		ncp_send_failure(c,COMPL_FAILURE);
		return;	
	}
}
