nuts4_141/
nuts4_141/n4/boards/0002/
nuts4_141/n4/boards/0003/
nuts4_141/n4/boards/0004/
nuts4_141/n4/boards/0006/
nuts4_141/n4/boards/0FFF/
nuts4_141/n4/etc/
nuts4_141/n4/pubgroups/0004/
nuts4_141/n4/pubgroups/0006/
nuts4_141/n4/users/0FFF/
nuts4_141/n4b/boards/0001/
nuts4_141/n4b/boards/0002/
nuts4_141/n4b/boards/0FFF/
nuts4_141/n4b/etc/
nuts4_141/n4b/pubgroups/0004/
nuts4_141/n4b/pubgroups/0005/
nuts4_141/n4b/src/
nuts4_141/n4b/users/0FFF/
/***************************************************************************
 FILE: cl_remote_user.cc
 LVU : 1.3.8

 DESC:
 This file contains the code for remote user specific actions. It also defines
 some command functions declared virtual in cl_user. 

 Copyright (C) Neil Robertson 2003,2004

  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.

 ***************************************************************************/

#include "globals.h"


/*** Constructor ***/
cl_remote_user::cl_remote_user(
	uint16_t uid,cl_server *svr,pkt_user_info *pkt): cl_user(0)
{
cl_server *svr2;
cl_group *grp;
cl_user *u;
cl_friend *frnd;
st_user_ban *gb;
int ret,dlen,snlen;
char *tmp,*sname;

// Set id's
id = uid;
orig_id = pkt->orig_uid; // ntohs() done in cl_server::recv_join()
remote_id = ntohs(pkt->uid);
desc = NULL;
full_name = NULL;
full_desc = NULL;
home_svrname = NULL;

stage = USER_STAGE_NEW;

if (svr->proto_rev > 5) {
	sname = (char *)pkt->name_desc_svr + pkt->namelen + pkt->desclen;
	snlen = pkt->len - (PKT_USER_INFO_SIZE-1) - pkt->namelen - pkt->desclen;
	dlen = pkt->desclen;
	}
else {
	sname = NULL;
	snlen = 0;
	dlen = pkt->len - pkt->namelen - (PKT_USER_INFO_SIZE-1);
	}

// Set name, description & home server name
if ((error = set_name((char *)pkt->name_desc_svr,pkt->namelen)) != OK ||
    (error = set_desc((char *)pkt->name_desc_svr + pkt->namelen,dlen)) != OK ||
    (error = set_svrname(sname,snlen)) != OK) 
	return;

stage = USER_STAGE_CMD_LINE;

// Set flags. Ignore muzzled, invis & net_bcast flags. These should never
// be sent by the other end but just in case someone has a hacked version...
prev_flags = flags = ntohl(pkt->user_flags) & REMOTE_IGNORE_FLAGS_MASK;

// Set misc. 
server_from = svr;
hop_count = pkt->hop_count+1;
orig_level = (pkt->orig_level > USER_LEVEL_ADMIN ? USER_LEVEL_ADMIN : pkt->orig_level);
term_cols = pkt->term_cols;
term_rows = pkt->term_rows;

group = NULL;
home_group = remotes_home_group;

// Set home ip. Data stored in network byte order. If we're first remote
// server then fill in ip address.
bzero((char *)&ip_addr,sizeof(ip_addr));
ip_addr.sin_family = AF_INET;
ip_addr.sin_port = pkt->home_port;
if (FLAGISSET(USER_FLAG_IP6_ADDR)) {
	log(1,"WARNING: User %04X (%04X@%s) has unsupported IP6 home address",
		id,remote_id,svr->name);
	ip_addr.sin_addr.s_addr = 0;
	ipnumstr = strdup("0.0.0.0");
	}
else {
	ip_addr.sin_addr.s_addr = pkt->home_addr.ip4;
	ipnumstr = strdup((char *)inet_ntoa(ip_addr.sin_addr));

	// See if user or server already has this address 
	if ((tmp = get_ipnamestr(ip_addr.sin_addr.s_addr,this))) {
		free(ipnamestr);
		ipnamestr = tmp;
		log(1,"User address %s = %s",ipnumstr,ipnamestr);
 		}
	else {
	// Create ip name resolve thread
	if (create_thread(
		THREAD_TYPE_USER_RESOLVE,
		&ip_thrd,user_resolve_thread_entry,(void *)this) == -1) {
			errprintf("Unable to create thread: %s\n\n",
				strerror(errno));
			log(1,"ERROR: UID %04X: Unable to create thread: %s",
				id,strerror(errno));
		}
}
	}

// Do logon message before level set to user
allprintf(
	MSG_MISC,
	USER_LEVEL_NOVICE,
	this,"~BGREMLOGON:~RS ~FT%04X~RS, %s %s\n",id,name,desc);

// Set some variables
level = (pkt->orig_level > remote_user_max_level) ?
         remote_user_max_level : pkt->orig_level;
type = USER_TYPE_REMOTE;
login_time = server_time;

// Send logon notify
FOR_ALL_SERVERS(svr2) {
	if (svr2->stage == SERVER_STAGE_CONNECTED) {
		if ((ret=svr2->send_logon_notify(id)) != OK)
			log(1,"ERROR: cl_remote_user::cl_remote_user() -> cl_server::send_logon_notify(): %s",err_string[ret]);
		}
	}

// See if we're a friend of anyone and if so then announce our logon.
FOR_ALL_USERS(u) {
	if (u == this) continue;

	FOR_ALL_USERS_FRIENDS(u,frnd) {
		// svr_name not set since the id is for a remote user on the
		// local server
		if (frnd->utype == USER_TYPE_LOCAL && frnd->id == id) {
			u->infoprintf("Your friend ~FT%04X~RS has logged on.\n",id);
			frnd->stage = FRIEND_ONLINE;
			frnd->local_user = this;
			}
		}
	}

// See if we're on any groups banned list and if so set user pointer. This
// is reset in ~cl_user().
FOR_ALL_GROUPS(grp) {
	for(gb=grp->first_ban;gb;gb=gb->next) {
		// gb->user may be set already is user has looped back
		if (gb->utype == USER_TYPE_REMOTE && gb->uid == orig_id) {
			if (!gb->user) gb->user = this;
			break;
			}
		}
	}

// Log it
log(1,"Remote user %04X (%s) from server '%s' joined.\n",
	id,name,server_from->name);

remote_user_count++;
}




/*** Destructor ***/
cl_remote_user::~cl_remote_user()
{
char *str;

// If stage still new then object creation failed, do nothing.
if (stage == USER_STAGE_NEW) return;

free(full_name);
FREE(home_svrname);
if (group) group->leave(this);

// Just in case thread is still running
cancel_thread(&ip_thrd);

allprintf(
	MSG_MISC,
	USER_LEVEL_NOVICE,
	this,"~BRREMLOGOFF:~RS ~FT%04X~RS, %s %s\n",id,name,desc);

str = (char *)(FLAGISSET(USER_FLAG_TIMEOUT) ? "timed out" : "left");
log(1,"Remote user %04X (%s) from server '%s' %s.\n",
	id,name,server_from->name,str);

remote_user_count--;
}



//////////////////////////////// I/O FUNCTIONS ///////////////////////////////

/*** Got input off the net ***/
int cl_remote_user::uread()
{
last_input_time = server_time;
UNSETFLAG(USER_FLAG_TIMEOUT_WARNING);

try {
	parse_line();
	}
catch(enum user_stages stg) {
	if (stg == USER_STAGE_DISCONNECT) disconnect();
	else {
		log(1,"INTERNAL ERROR: Caught unexpected user_stage %d in cl_remote_user::uread()",stg);
		return ERR_INTERNAL;
		}
	}
return OK;
}




/*** Write formatted string to remote user ***/
void cl_remote_user::uprintf(char *fmtstr,...)
{
char str[ARR_SIZE];
va_list args;

va_start(args,fmtstr);
vsnprintf(str,ARR_SIZE,fmtstr,args);
va_end(args);

if (server_from->send_print(remote_id,0,str) != OK)
	log(1,"ERROR: Failed to send remote print to %04X@%s",id,server_from->name);
}



///////////////////////////// MISCELLANIOUS FUNCTIONS  ///////////////////////


/*** Set users name and full_name ***/
int cl_remote_user::set_name(char *nme)
{
char *tmp,*s;

if (!nme) nme = "";

// Don't allow user to call themselves SYSTEM or allow print codes in the name
if (!strcmp(nme,SYS_USER_NAME) || has_printcode(nme)) return ERR_INVALID_NAME;

// Don't allow invalid characters
for(s = nme;*s;++s) if (*s < 32 || *s == '@') return ERR_INVALID_NAME;

if (!(tmp = strdup(nme))) return ERR_MALLOC;
FREE(full_name);
full_name = tmp;

// Allocate again , dont' want name and full_name pointing to same address
if (!(tmp = strdup(nme))) return ERR_MALLOC;
if ((int)strlen(tmp) > max_name_len) tmp[max_name_len] = '\0';
FREE(name);
name = tmp;
name_key = generate_key(name);
return OK;
}




/*** Set the users name (limited to max_name_len) and full_name (used when
     passing info to next hop servers) using non-null terminated string ***/
int cl_remote_user::set_name(char *nme, int nlen)
{
char *tmp,*s;

if (!(tmp = (char *)malloc(nlen + 1))) return ERR_MALLOC;
FREE(full_name);
full_name = tmp;
memcpy(full_name,nme,nlen);
full_name[nlen]='\0';

if (!strcmp(full_name,SYS_USER_NAME) || has_printcode(full_name)) {
	free(full_name);
	return ERR_INVALID_NAME;
	}
for(s = full_name;*s;++s) {
	if (*s < 32 || *s == '@') {
		free(full_name);
		return ERR_INVALID_NAME;
		}
	}

if (nlen > max_name_len) nlen = max_name_len;
if (!(tmp = (char *)malloc(nlen + 1))) return ERR_MALLOC;
FREE(name);
name = tmp;
memcpy(name,nme,nlen);
name[nlen]='\0';
name_key = generate_key(name);
return OK;
}




/*** Set desc using given length ***/
int cl_remote_user::set_desc(char *dsc, int dlen)
{
char *tmp;

if (!(tmp = (char *)malloc(dlen + 1))) return ERR_MALLOC;
FREE(full_desc);
full_desc = tmp;
memcpy(full_desc,dsc,dlen);
full_desc[dlen]='\0';

if (dlen > max_desc_len) dlen = max_desc_len;
if (!(tmp = (char *)malloc(dlen + 1))) return ERR_MALLOC;
free(desc);
desc = tmp;
memcpy(desc,dsc,dlen);
desc[dlen]='\0';
return OK;
}




/*** The the users original server name ***/
int cl_remote_user::set_svrname(char *snme, int snlen)
{
char *tmp;

if (!snlen) {
	FREE(home_svrname);  return OK;
	}
if (!(tmp = (char *)malloc(snlen + 1))) return ERR_MALLOC;
FREE(home_svrname);
home_svrname = tmp;
memcpy(home_svrname,snme,snlen);
home_svrname[snlen] = '\0';
return OK;
}




/*** Send leave packet and delete ourself. This is used by local server to
     get rid of a remote user object (unless we received LEFT packet in which
     case object can just be deleted) ***/
void cl_remote_user::disconnect()
{
int ret;

uprintf("\n~FTLeaving this server...\n\n");

if ((ret=server_from->send_left(remote_id)) != OK)
	log(1,"ERROR: cl_remote_user::disconnect() -> cl_server::send_left(): %s",
		err_string[ret]);

delete this;
}





///////////////////////// VIRTUAL FUNCTION COMMANDS ////////////////////////

/*** Leave the server/group ***/
void cl_remote_user::com_leave()
{
throw USER_STAGE_DISCONNECT;
}




void cl_remote_user::com_profile()
{
uprintf("Remote users cannot enter a profile.\n");
}




void cl_remote_user::com_mail()
{
uprintf("Remote users cannot use the mail system.\n");
}




void cl_remote_user::com_setemail()
{
uprintf("Remote users do not need to set an email address.\n");
}




void cl_remote_user::com_stgroup()
{
uprintf("Remotes users cannot set a starting group.\n");
}




void cl_remote_user::com_suicide()
{
uprintf("Remote users cannot suicide.\n");
}




void cl_remote_user::com_batch()
{
uprintf("Remote users do not have command batches.\n");
}




void cl_remote_user::com_lsbatches()
{
uprintf("Remote users do not have command batches.\n");
}