/***************************************************************************
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");
}