
/*
    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 <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <ctype.h>
#include <sys/utsname.h>
#include "nwcrypt.h"
#include "ncp.h"
#include "logging.h"
#include "util.h"
#include "handlers.h"
#include "bindops.h"

static int desc_len=7;
static char description[1024]="(none)";

struct scan_bind_hlp
{
	int after;
	struct ncp_scan_bind_rq* rq;
};

int bind_type_cmp(word t1,word t2)
{
	return t1==BTYPE_ALL || t2==BTYPE_ALL || t1==t2;
}

#define WILD_NAME "*"

static int bind_name_cmp(char* s1,char* s2)
{
	return strcasecmp(s1,s2)==0 || strcmp(s1,WILD_NAME)==0 || strcmp(s2,WILD_NAME)==0;
}

static int scan_bind_find(bind_list* bl,void* data)
{
	struct scan_bind_hlp* sbh=(struct scan_bind_hlp*)data;
	
	if (!sbh->after)
	{
		LPRINTF(LL_DEBUG,("scan_bind_find: last_id: %u obj_id: %u\n",ntohl(sbh->rq->last_id),bl->obj.id));
		if (ntohl(sbh->rq->last_id)==bl->obj.id) sbh->after=1;
		return 0;
	}
	LPRINTF(LL_DEBUG,("t1: %d t2: %d n1: %s n2: %s\n",bl->obj.type,
		ntohs(sbh->rq->type),sbh->rq->name,bl->obj.name.name));
	if (bind_type_cmp(bl->obj.type,ntohs(sbh->rq->type)) &&
	    bind_name_cmp(sbh->rq->name,bl->obj.name.name))
		return 1;
	return 0;
}

static int key_log_find(bind_list* bl,void* data)
{
	struct ncp_key_login_rq* rq=(struct ncp_key_login_rq*)data;

	if ( bind_type_cmp(bl->obj.type,ntohs(rq->type)) &&
	     strcasecmp(rq->name,bl->obj.name.name)==0)
		return 1;
	return 0;
}

static int get_id_find(bind_list* bl,void* data)
{
	struct ncp_get_bind_id_rq* rq=(struct ncp_get_bind_id_rq*)data;

	if ( bind_type_cmp(bl->obj.type,ntohs(rq->type)) &&
	     strcasecmp(rq->name,bl->obj.name.name)==0)
		return 1;
	return 0;
}

int get_name_find(bind_list* bl,void* data)
{
	if (bl->obj.id==(*(dword*)data)) return 1;
	return 0;
}

static int read_prop_find(bind_list* bl,void* data)
{
	struct ncp_read_prop_rq* rq=(struct ncp_read_prop_rq*)data;

	if ( bind_type_cmp(bl->obj.type,ntohs(rq->type)) &&
	     strcmp(rq->obj.name,bl->obj.name.name)==0)
		return 1;
	return 0;
}

static void get_station_info(connection* c,dword cid)
{
	LPRINTF(LL_FULL,("Get station info: cid=%li\n",cid));
	if (cid<MAX_CONNECT)
	{
		struct ncp_log_info* lgi=(struct ncp_log_info*)get_conn_buf(c);
		connection* cn=get_conn(cid);
		
		if (!cn->alloc || cn->logged_obj==NULL)
		{
			memset(lgi,0,sizeof(*lgi));
		}
		else
		{
			lgi->id=htonl(cn->logged_obj->id);
			lgi->type=htons(cn->logged_obj->type);
			lgi->name=cn->logged_obj->name;
			lgi->log_time=cn->login_time;
			lgi->reserved=0;
		}
		ncp_send(c,sizeof(*lgi));
		return;
	}
	ncp_send_failure(c,COMPL_FAILURE);
	return;
}

static void get_internet_address(connection* c,dword cid)
{
	struct ncp_net_address* rpl=(struct ncp_net_address*)get_conn_buf(c);
	
	if (cid<MAX_CONNECT)
	{
		connection* cn=get_conn(cid);
		if (cn->alloc)
		{
			rpl->net=cn->peer_addr.sipx_network;
			ipx_assign_node(rpl->node,cn->peer_addr.sipx_node);
			rpl->port=cn->peer_addr.sipx_port;
			ncp_send(c,sizeof(*rpl));
			return;
		}
	}
	ncp_send_failure(c,COMPL_FAILURE);
	return;
}

void ncp_handle_r23(int subfunc,int size,char* data,connection* c)
{
	switch(subfunc)
	{
	case 17: /* get file server information - mostly hardwired */
	{
		struct ncp_fs_info* fsi=(struct ncp_fs_info*)get_conn_buf(c);
		
		fsi->name=server_name;
		fsi->vers_hi=3;
		fsi->vers_lo=11;
		fsi->max_con=htons(MAX_CONNECT-1);
		fsi->con_used=0;
		{
		int p;
		for(p=1;p<MAX_CONNECT;p++)
			if (get_conn(p)->alloc) fsi->con_used++;
		}
		fsi->con_used=htons(fsi->con_used);
		fsi->volumes=htons(MAX_VOLUME);
		fsi->revis_lvl=0; 
		fsi->SFT_lvl=2;
		fsi->TTS_lvl=1;
		fsi->max_con_used=htons(MAX_CONNECT-ntohs(fsi->con_used)-2);
		fsi->acc_ver=1;
		fsi->vap_ver=1;
		fsi->que_ver=1;
		fsi->ps_ver=1;
		fsi->vc_ver=1;
		fsi->sec_ver=1;
		fsi->br_ver=1;
		memset(fsi->reserved,0,sizeof(fsi->reserved));
		ncp_send(c,sizeof(*fsi));
		return;
	}
	case 19: /* get internet address (max. 255)*/
	{
		get_internet_address(c,*(byte*)data);
		return;
	}
	case 21: /* get object connect list */
	{
		struct ncp_get_bind_id_rq* rq=(struct ncp_get_bind_id_rq*)data;
		struct ncp_con_list* clist=(struct ncp_con_list*)get_conn_buf(c);
		int cid;
		
		rq->name[rq->name_len]=0;
		clist->len=0;
		for(cid=1;cid<MAX_CONNECT;cid++)
		{
			connection* p=get_conn(cid);
			if (p->alloc && p->logged_obj!=NULL &&
			    p->logged_obj->type==ntohs(rq->type) &&
			    strcasecmp(p->logged_obj->name.name,rq->name)==0)
			{
				clist->list[clist->len++]=cid;
			}
		}
		ncp_send(c,clist->len+sizeof(clist->len));
		return;
	}
	case 22: /* get logged station's info (max. 255) */
	{
		get_station_info(c,*(byte*)data);
		return;
	}
	case 23: /* get log key */
	{
		struct ncp_log_key* lgk=(struct ncp_log_key*)get_conn_buf(c);
		char* k;
		
		for(k=lgk->key;k<lgk->key+sizeof(lgk->key);k++)
		{
			*k=rand();
		}
		LPRINTF(LL_DEBUG,("get log key: %i should be 8\n",k-lgk->key));
		c->last_key=*lgk;
		ncp_send(c,sizeof(*lgk));
		return;
	}
	case 24: /* keyed login, NW 3.11 authorization */
	{
		struct ncp_key_login_rq* rq=(struct ncp_key_login_rq*)data;
		bind_list* bl;
		lwpassword passwd;
		
		rq->name[rq->name_len]=0;
		bl=bind_first_that(key_log_find,data);
		if (bl!=NULL && bl->obj.password(&(bl->obj),&passwd)==BPASS_OK)
		{
			struct ncp_log_key key=c->last_key;
			byte oid[4];
			
			oid[0]=(bl->obj.id>>24)&0xFF;
			oid[1]=(bl->obj.id>>16)&0xFF;
			oid[2]=(bl->obj.id>>8)&0xFF;
			oid[3]=bl->obj.id&0xFF;
			p2EC(key.key,passwd.passwd,key.key);
			if (memcmp(key.key,rq->key.key,sizeof(key))==0) /* success */
			{
				LOG_START(LL_INFO)
				log_printf("login: %s conn: %i addr: ",bl->obj.name.name,get_conn_id(c));
				ipx_fprint_saddr(log_file,&(c->peer_addr));
				log_printf("\n");
				LOG_END
				c->logged_obj=&(bl->obj);
				ncp_get_date(&(c->login_time));
				ncp_send_ok(c);
			}
			else
			{
				LOG_START(LL_INFO)
				log_printf("login failure: %s conn: %i addr: ",bl->obj.name.name,get_conn_id(c));
				ipx_fprint_saddr(log_file,&(c->peer_addr));
				log_printf("\n");
				LOG_END
				ncp_send_failure(c,COMPL_FAILURE);
			}
			return;
		}
		ncp_send_failure(c,COMPL_NOT_OBJECT);
		return;
	}
	case 26: /* get internet address (max. 2^32-1)*/
	{
		get_internet_address(c,*(dword*)data);
		return;
	}
	case 27: /* get object connect list 32*/
	{
		struct ncp_get_con_list32* rq=(struct ncp_get_con_list32*)data;
		struct ncp_con_list32* clist=(struct ncp_con_list32*)get_conn_buf(c);
		int cid;
		
		rq->name.name[rq->name.name_len]=0;
		clist->len=0;
		for(cid=rq->last_cid+1;cid<MAX_CONNECT;cid++)
		{
			connection* p=get_conn(cid);
			if (p->alloc && p->logged_obj!=NULL &&
			    p->logged_obj->type==ntohs(rq->type) &&
			    strcasecmp(p->logged_obj->name.name,rq->name.name)==0)
			{
				clist->list[clist->len++]=cid;
			}
		}
		ncp_send(c,clist->len*sizeof(clist->list[0])+sizeof(clist->len));
		return;
	}
	case 28: /* get logged station's info (max. 2^32-1) */
	{
		get_station_info(c,*(dword*)data);
		return;
	}
	case 53: /* get bindery object ID */
	{
		struct ncp_get_bind_id_rq* rq=(struct ncp_get_bind_id_rq*)data;
		bind_list* bl;
				
		rq->name[rq->name_len]=0;
		bl=bind_first_that(get_id_find,data);
		if (bl!=NULL)
		{
			struct ncp_object_name_rpl* obj=(struct ncp_object_name_rpl*)get_conn_buf(c);
			obj->id=htonl(bl->obj.id);
			obj->type=htons(bl->obj.type);
			obj->name=bl->obj.name;
			ncp_send(c,sizeof(*obj));
			return;
		}
		ncp_send_failure(c,COMPL_NOT_OBJECT);
		return;
	}
	case 54: /* get bindery object name */
	{
		dword rqid=ntohl(*(dword*)data);
		bind_list* bl;
		
		bl=bind_first_that(get_name_find,&rqid);
		if (bl!=NULL)
		{
			struct ncp_object_name_rpl* obj=(struct ncp_object_name_rpl *)get_conn_buf(c);

			obj->id=htonl(bl->obj.id);
			obj->type=htons(bl->obj.type);
			obj->name=bl->obj.name;
			ncp_send(c,sizeof(*obj));
			return;
		}
		ncp_send_failure(c,COMPL_NOT_OBJECT);
		return;
	}
	case 55: /* scan bindery object */
	{
		struct scan_bind_hlp sbh;
		bind_list* bl;
		
		sbh.rq=(struct ncp_scan_bind_rq*)data;
		LPRINTF(LL_DEBUG,("scan bindery object: last_id: %u NOT_LOGGED: %u cond:%i\n",sbh.rq->last_id,NOT_LOGGED,sbh.rq->last_id==NOT_LOGGED));
		sbh.after=(sbh.rq->last_id==NOT_LOGGED);
		sbh.rq->name[sbh.rq->name_len]=0;
		bl=bind_first_that(scan_bind_find,&sbh);
		if (bl!=NULL)
		{
			LPRINTF(LL_DEBUG,("found type: %d\n",bl->obj.type));
			switch(bl->obj.type)
			{
			case BTYPE_SERVER:
			case BTYPE_USER:
			{
				struct ncp_scan_bind_rpl* obj=(struct ncp_scan_bind_rpl *)get_conn_buf(c);

				obj->id=htonl(bl->obj.id);
				obj->type=htons(bl->obj.type);
				obj->name=bl->obj.name;
				obj->flag=bl->obj.flag;
				obj->security=64; /* hard wired */
				obj->has_prop=255;
				ncp_send(c,sizeof(*obj));
				return;
			}
			default:LPRINTF(LL_FULL,("Unsupported object type\n"));
				ncp_send_failure(c,COMPL_FAILURE);
				return;
			}
		}
		ncp_send_failure(c,COMPL_NOT_OBJECT);
		return;
	}
	case 61: /* read property value */
	{
		struct ncp_read_prop_rq* rq=(struct ncp_read_prop_rq*)data;
		struct ncp_read_prop_rpl* rpl=(struct ncp_read_prop_rpl*)get_conn_buf(c);
		bind_list* bl;
		
		rq->obj.name[rq->obj.name_len]=0;
		LPRINTF(LL_FULL,("read prop value for object %s\n",rq->obj.name));
		bl=bind_first_that(read_prop_find,rq);
		if (bl!=NULL)
		{
			object_property* prop;
			struct ncp_string* pname=(struct ncp_string*)(rq->obj.name+rq->obj.name_len+1);

			pname->name[pname->name_len]=0;
			LPRINTF(LL_FULL,("prop name %s\n",pname->name));
			prop=prop_byname(&(bl->obj),pname->name);
			if (prop!=NULL)
			{
				memcpy(rpl->data,prop->value.raw,sizeof(rpl->data));
				rpl->more=0;
				rpl->flag=1; /* hard wired */
				ncp_send(c,sizeof(*rpl));
			}
			else
			{
				ncp_send_failure(c,COMPL_FAILURE); /* fix: no such property */
			}
			return;
		}
		ncp_send_failure(c,COMPL_NOT_OBJECT);
		return;
	}
	case 62: /* write property value */
	{
		/* dummy */
		ncp_send_ok(c);
		return;
	}
	case 70: /* get bindery access level */
	{
		struct ncp_bind_acc_lvl_rpl* bac=(struct ncp_bind_acc_lvl_rpl*)get_conn_buf(c);
		
		if (c->logged_obj!=NULL)
		{
			bac->security=17; /* logged R/W ? */
			bac->id=htonl(c->logged_obj->id);
		}
		else
		{
			bac->security=0; /* any R/W ? */
			bac->id=NOT_LOGGED;
		}
		ncp_send(c,sizeof(*bac));
		return;
	}
	case 200: /* check console privileges (none of course) */
	{
		ncp_send_failure(c,COMPL_NO_CON_RIGHTS);
		return;
	}
	case 201: /* get server description strings */
	{
		char* str=(char*)get_conn_buf(c);
		
		memcpy(str,description,desc_len);
		ncp_send(c,desc_len);		
		return;
	}
	default: LPRINTF(LL_FULL,("Unknown function (23,%d)\n",subfunc));
		ncp_send_failure(c,COMPL_FAILURE);
		return;	
	}
}

void get_server_name()
{
	struct utsname info;
#if 0
	struct hostent* he;
#endif
	
	if (uname(&info)!=0)
	{
		LPRINTF(LL_FATAL,("uname: %s\n",strerror(errno)));
		exit(1);
	}
#if 0
	he=gethostbyname(info.nodename);
	if (he==NULL)
	{
		LPRINTF(LL_FATAL,("gethostbyname: %s\n",strerror(errno)));
		exit(1);
	}
#endif

	strncpy(server_name.name,info.nodename/*he->h_name*/,sizeof(server_name.name)-1);
	server_name.name[sizeof(server_name.name)-1]=0;
	strtoupper(server_name.name);
}

void set_desc_strings()
{
 	char* p;
	struct utsname info;
	struct hostent* he;
	
	if (uname(&info)!=0)
	{
		LPRINTF(LL_FATAL,("uname: %s\n",strerror(errno)));
		exit(1);
	}
	he=gethostbyname(info.nodename);
	if (he==NULL)
	{
		LPRINTF(LL_FATAL,("gethostbyname: %s\n",strerror(errno)));
		exit(1);
	}


        sprintf(description,"Silicon Hill,");
        p=description+strlen(description)+1;
        sprintf(p,"%s %s on %s",info.sysname,info.release,he->h_name);
        p=p+strlen(p)+1;
        sprintf(p,"%s",__DATE__);
        p=p+strlen(p)+1;
        sprintf(p,"(C) Ales Dryak, 1994, 95");
        p=p+strlen(p)+1;
        desc_len=p-description;
}
