/* convers server - based on conversd written by DK5SG
 * ported to WNOS by DB3FL - 9109xx/9110xx
 * ported to NOS by PE1NMB - 920120
 * Mods by PA0GRI
 * Cleanup, and additional mods by WG7J
 * Major additions, rewrites, enhancements by KO4KS
 */

#ifdef MSDOS
#define extern extern far
#endif
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#ifdef	UNIX
#include <sys/types.h>
#include <sys/stat.h>
#endif
#ifdef MSDOS
#include <io.h>
#else
#define DFAR
#endif
#include "global.h"
#include "config.h"
#ifdef MAILBOX
#include "mailbox.h"
#include "netuser.h"
#endif
#include "pktdrvr.h"
#include "timer.h"
#include "cmdparse.h"
#include "socket.h"
#include "session.h"
#include "files.h"
#include "ax25.h"
#ifdef LZW
#include "lzw.h"
#endif
#include "usock.h"

char stars[] = "*** ";

#ifdef CONVERS

#pragma option -zEMYFAR

#define	LINK	1
#define space


#if defined(LZW) && !defined(UNIXJNOS)
void togglelzw __ARGS((int soc, int mode));
#endif

int Sconv = -1;
static int ConvNet0 = 0;
static int ConvHeader = 0;
static int TimeStamp = 0;
extern char Chostname[];
extern char CConsole[];
extern char shortversion[];
#ifndef UNIX 
extern int Conflogins;
#else
int Conflogins = 0;
#endif
#ifdef space
static char cnumber[] = "*** Channel numbers must be in the range 0..%d.\n";
#else
static char cnumber[] = "* range 0..%d.\n";
#endif

static char DFAR trailer[] = "***\n";
static char DFAR string15[] = "%*s %15s";
static char msgtext[] = "<*%s*>: %s\n";
static char DFAR theysigned[] = "%s%s signed o%s at %s.\n";
static char DFAR theyswitched[] = "%s%s switched to channel %d at %s.\n";
static char DFAR conversd[] = "conversd";
static char DFAR fullstr[] = "%s";
static char DFAR fullstrcr[] = "%s\n";
static char sosorry[] = "*** Sorry, ";
static char DFAR sorry[] = "%s%s Net on %s %d!\n";
static char DFAR busystr[] = "%s'%s' is already a %s!\n";
static char DFAR logfilestr[] = "%sLogfile is %sed\n";
static char DFAR netcontrolstr[] = "Net Control";
static char DFAR privatestr[] = "Closed";
static char DFAR clearedstr[] = "cleared";
static char DFAR passwordstr[] = "Password";
static char DFAR nowchannel[] = "%sNow on %s %d (%d user%s).\n";
static char DFAR nicknamestr[] = "%s%s%sset to '%s'.\n";
static char DFAR channelstr[] = "Channel";
static char DFAR systemstr[] = "SYSTEM";
static char unknownstr[] = "*** Unknown ";
static char DFAR unkcmdstr[] = "%scommand '/%s'.%s";
static char DFAR nouserstr[] = "%suser!\n";
static char DFAR gethelpstr[] = " Type /HELP for help.\n";
static char DFAR noopenstr[] = "Can't open '%s'\n";
static char DFAR activenetstr[] = "%sActive Net '%s'\n";
static char DFAR net2str[] = "%sNet %s %s\n";
static char DFAR conferencestr[] = "*** TNOS Conference @ ";
static char DFAR namecmdstr[] = "%sPlease login with '/n <call>'\n";
static char DFAR nonetstr[] = "%sNo Net!\n";
static char DFAR recheckstr[] = "*** Recheck";
static char DFAR questionstr[] = "*** Question";
static char DFAR nicknmstr[] = "Nickname ";
static char DFAR tmstr[] = "Time";
static char DFAR userstr[] = "User";
static char DFAR hoststr[] = "Host";
static char DFAR newuserstr[] = "%sNew user '%s' has entered conference area on channel '%d'";
static char DFAR urnotstr[] = "%sYou are not %s\n";
static char DFAR urassigned[] = "%s'%s' has assigned you as %s on %s %d\n";
static char DFAR wrongchannel[] = "%s%s '%s' not on this %s\n";
static char DFAR openminutes[] = "%sMinutes starting at %s\n%sNet '%s' - %s is '%s'\n";
static char DFAR closeminutes[] = "%sMinutes ending at %s\n";
static char DFAR headerstr[] = "User       Host       Via         Channel  Time Personal\n";
static char DFAR userdata[] = "%-10.10s %-10.10s %-11.11s %6d %s %s\n";
static char DFAR hinvi[] = "/\377\200INVI %s %s %d %s\n";
static char DFAR uaddstr[] = "/\377\200UADD %s %s %s %d %s\n";
static char DFAR bumpstr[] = "/\377\200BUMP %s %d\n";
static char DFAR ndatstr[] = "/\377\200NDAT %d %d %d %d %d %s|%s|%s|\n";
static char DFAR quesstr[] = "/\377\200QUES %d %c %s %s\n";
static char DFAR umsgstr[] = "/\377\200UMSG %s %s %s\n";
static char DFAR cmsgstr[] = "/\377\200CMSG %s %d %s\n";
static char DFAR hhoststr[] = "/\377\200HOST %s %s %s\n";
static char DFAR huserstr[] = "/\377\200USER %s %s %ld %d %d %s\n";
static char DFAR loopstr[] = "/\377\200LOOP %s %s %s\n";
static char DFAR SYSCOLORS[] = "0C";
static char DFAR TEXTCOLORS[] = "09";
static char DFAR INFOCOLORS[] = "0B";
#if 0
static char DFAR INPUTCOLORS[] = "0F";
#endif
static char DFAR summary[] = "%sTNOS Conference Bridge command summary:\n ";
static char DFAR modeis[] = "Mode is: %s\n";
static char DFAR acceptstr[] = "Accept";
static char DFAR refuse[] = "Refuse";
static char DFAR redun[] = "Conf. redundancy timer (sec)";
static char DFAR entrystr[] = "Conf. entry channel";
static char DFAR givehead[] = "Display login header on connect";
static char DFAR allownets[] = "Allow Nets on Channel 0";
static char DFAR timestampstr[] = "Timestamp all user messages";
static char DFAR maxwaitstr[] = "Re-link max wait (sec)";
static char DFAR hostqueuestr[] = "Max. Host Queue (bytes)";
static char DFAR userqueuestr[] = "Max. User Queue (bytes)";
static char DFAR motdstr[] = "Conference MOTD: %s\n";
static char ff_str[] = "ff";
static char n_str[] = "n";
static char empty[] = "";
static char DFAR timestampfmt[] = "<*%s:%s*>:";
static char DFAR nontimestampfmt[] = "<*%s*>:";
static char DFAR timestampfmt2[] = "<%s:%s>:";
static char DFAR nontimestampfmt2[] = "<%s>:";
static char DFAR alreadyon[] = "%s%s %s is already on this channel.";
static char DFAR exitingstr[] = "Exiting %s%s\n";
static char DFAR welcome[] = "%s%s%sNet '%s' - %s is '%s'... Welcome, %s!\n";
static char DFAR welcomeback[] = "%sWelcome back, %s\n";
static char DFAR noinvite[] = "%s%s net, only %s can invite\n";
#ifndef UNIX
static char DFAR linkhdr[] = "Host             State         Since%s\n";
static char DFAR linkhdr2[] = " NextTry Tries Queue Receivd Xmitted";
#endif
static char DFAR nosuchuser[] = "%sNo such user: %s.\n";
static char DFAR online[] = "%sThere are %d users online\n";
static char DFAR groupsavail[] = "%sThere %s %d group%s available\n";
static char DFAR colorstatus[] = "%sCurrently ANSI Color graphics are O%s\n";
static char DFAR personalset[] = "%sPersonal data set to: %s\n";
static char DFAR quotebanner[] = "*** Quote of the day:\n";
static char DFAR whobanner[] = "%s             %s  %s Personal\n";
static char DFAR rollstr[] = "%s'%s' has rolled a %d and a %d for a total of %d! %s";
static char DFAR cutstr[] = "%s'%s' has cut the deck and selected the %s of %s! %s";
static char DFAR gloghdr[] = "*** Current Check-ins\n";
static char DFAR glogstr[] = "    %s     %s         In    %s\n";
static char DFAR grouphdr[] = "%sAvailable Groups:\n Channel  Group Name\n =======  ==========\n";

#ifdef space
static char DFAR youare[] = "%sYou are %son channel %d.\n";
static char DFAR amessage[] = "\n*** Message from ";
static char DFAR invitetext[] = "%s%s at %s ...\n%sPlease join conference channel %d.\n";
static char DFAR mbinvitetext[] = "%s%s at %s ...\n%sPlease type 'CONF %d' to join conference channel %d.\n";
static char DFAR responsetext[] = "%sInvitation sent to %s @ %s";
#else
static char DFAR youare[] = "%sOn channel %d.\n";
static char DFAR amessage[] = "\n*** Msg frm ";
static char DFAR invitetext[] = "%s%s at %s ...\n%sPse join ch. %d.\n";
static char DFAR mbinvitetext[] = "%s at %s ...\n%sPse hit 'CONF %d' for conference ch. %d.\n";
static char DFAR responsetext[] = "%ssent to %s @ %s";
#endif

static char myfeatures[] = "un";

#define MAXCHANNEL 	((int) 32767)
#define LINELEN 	256
#define MAX_WAITTIME	(60*60*3)
#define NAMELEN 16

struct convection {
	int  type;			/* Connection type */
#define CT_UNKNOWN      0
#define CT_USER         1
#define CT_HOST         2
#define CT_CLOSED       3
    char  name[NAMELEN+1];         /* Name of user or host */
    char  host[NAMELEN+1];         /* Name of host where user is logged on */
    char  nickname[NAMELEN+1];		/* Nickname of user */
    char  password[NAMELEN+1];		/* Password of user */   
	struct convection *via;		/* Pointer to neighbor host */
    char *data;                 /* room for some personal data */
    int channel;            /* Channel number */
	int net;			/* Channel of controlled net */
	int32 nettime;			/* Time entered channel (used by nets) */
	int32 time;			/* Connect time */
	int maxq;           /* Maximum outstanding data before link reset */
	int locked;			/* Set if mesg already sent */
	int fd;				/* Socket descriptor */
	int flags;			/* User flags */
#define CLOSE_SOCK  1
#define USE_SOUND   2
#define CHANGED_INFO 4
#define USE_LZW 8
#define USE_COLOR 16
	char colorset[2];		/* current color set */
  int  features;
#define FEATURE_AWAY    1       /* a - "away feature" */
#define FEATURE_FWD     2       /* d - "destination forwarding" */
#define FEATURE_MODES   4       /* m - "channel modes" */
#define FEATURE_LINK    8       /* p - "ping pong link measurement" */
#define FEATURE_UDAT    16      /* u - "udat command extension and user command understood both" */
#define FEATURE_NICK    32      /* n - "TNOS Nickname extensions" */
    /* This buffer is only needed for local users; a lot of space is wasted
     * for users from other hosts (256 bytes per user!). Fixed 930728 - WG7J
     * char ibuf[LINELEN];
     */
	char *ibuf;                      /* Input buffer */
	int  received;			/* Number of bytes received */
	int  xmitted;			/* Number of bytes transmitted */
	int  paged;			/* Last channel invited to (paged) */
	struct convection *next;	/* Linked list pointer */
};

#define CM_UNKNOWN	(1 << CT_UNKNOWN)
#define CM_USER		(1 << CT_USER)
#define CM_HOST		(1 << CT_HOST)
#define CM_CLOSED	(1 << CT_CLOSED)

#define NULLCONNECTION	((struct convection *) 0)

static struct convection *convections;

struct permlink {
    char name[NAMELEN+1];   /* Name of link */
    int32 addr;             /* address to link to */
	struct convection *convection;	/* Pointer to associated connection */
    int32 statetime;        /* Time of last (dis)connect */
	int  tries;			/* Number of connect tries */
	int32 waittime;			/* Time between connect tries */
	int32 retrytime;		/* Time of next connect try */
	int fd;				/* socket descriptor */
	struct permlink *next;		/* Linked list pointer */
};
#define NULLPERMLINK ((struct permlink *) 0)

static struct permlink *permlinks;

struct filter_link {
    struct filter_link *next;
    int32 addr;
};
#define NULLFL ((struct filter_link *) 0)

static struct filter_link *Filterlinks;
static int FilterMode;

#ifdef LINK
struct proc *Linker;
static void connect_permlinks __ARGS((int a,void *b,void *c));
static void update_permlinks __ARGS((char *name,struct convection *cp));
#endif

extern char *getquote __ARGS((void));
static void cmdsummary_command __ARGS((struct convection *cp));
static void personal_command __ARGS((struct convection *cp));
static void nickname_command __ARGS((struct convection *cp));
static void convcolorchange __ARGS((struct convection *p, unsigned char *str));
static void roll_command __ARGS((struct convection *cp));
static void cut_command __ARGS((struct convection *cp));
void save_personal __ARGS((struct convection *cp));
void conv_incom __ARGS((int s,void *t,void *p));
#ifndef UNIXJNOS
extern void statlog __ARGS((char *buf));
#endif
int conv0 __ARGS((int argc,char *argv[],void *p));
int conv1 __ARGS((int argc,char *argv[],void *p));
#ifdef UNIX
#define CREATE_TEXT "w+"
#define UPDATE_TEXT "r+"
extern int dosetflag __ARGS((int argc,char *argv[],void *p,int flagtoset, int AX25only));
extern int deleteserver __ARGS((int *mysocket));
extern int installserver __ARGS((int argc,char *argv[],int *mysocket,char *listener,int defport,char *procname,void (*proc)__ARGS((int,void *,void *)),int size,void *parm));
#endif
static void free_connection __ARGS((register struct convection *cp));
static void free_closed_connections __ARGS((void));
static struct convection *alloc_connection __ARGS((int fd));
void check_buffer_overload __ARGS((void));
static void clear_locks __ARGS((void));
static void send_sounds __ARGS((struct convection *p));
static char *timestring __ARGS((long  gmt));
static void send_user_change_msg __ARGS((char *name,char *host,long time,int oldchannel,int newchannel,char *pers));
static char *formatline __ARGS((char *prefix,char *text));
static void send_msg_to_user __ARGS((char *fromname,char *toname,char *text));
static void send_msg_to_channel __ARGS((char *fromname,int channel,char *text));
static void send_invite_msg __ARGS((char *fromname,char *toname,int channel,char *msg));
static void time_command __ARGS((struct convection *cp));
static int onchannel __ARGS((int channel));
static void mystatus __ARGS((struct convection *cp,int old));
static void channel_command __ARGS((struct convection *cp));
static int gatekeeper __ARGS((struct convection *cp,int channel));
static void accept_command __ARGS((struct convection *cp));
static char *skipone __ARGS((char *cptr, int times));
static void help_command __ARGS((struct convection *cp));
static void news_command __ARGS((struct convection *cp));
static void invite_command __ARGS((struct convection *cp));
static void links_command __ARGS((struct convection *cp));
static void msg_command __ARGS((struct convection *cp));
static void announce_new_user __ARGS((struct convection *cp));
static void color_command __ARGS((struct convection *cp));
void set_personal __ARGS((struct convection *cp));
static void name_command __ARGS((struct convection *cp));
#ifdef LZW
static void compressed_command __ARGS((struct convection *cp));
#endif
static void sounds_command __ARGS((struct convection *cp));
static void update_user_data __ARGS((struct convection *cp,int personal));
static void password_command __ARGS((struct convection *cp));
int isrosedigit __ARGS((char c));
static char *getVia __ARGS((char *call));
int CountConfUsers __ARGS((void));
int CountConfGroups __ARGS((void));
static void quote_command __ARGS((struct convection *cp));
static void who_command __ARGS((struct convection *cp));
static void whois_command __ARGS((struct convection *cp));
static void realname_command __ARGS((struct convection *cp));
static void h_cmsg_command __ARGS((struct convection *cp));
int Allow_host __ARGS((int s));
static void h_host_command __ARGS((struct convection *cp));
static void h_invi_command __ARGS((struct convection *cp));
static void h_loop_command __ARGS((struct convection *cp));
void h_uadd_command __ARGS((struct convection *cp));
static void initialusers __ARGS((int channel));
void h_ndat_command __ARGS((struct convection *cp));
void h_ques_command __ARGS((struct convection *cp));
static void h_umsg_command __ARGS((struct convection *cp));
static void h_bump_command __ARGS((struct convection *cp));
static void h_user_command __ARGS((struct convection *cp));
static void gname_command __ARGS((struct convection *cp,char *cptr));
static void assignnet_command __ARGS((struct convection *cp,char *cptr));
static void bumpnet_command __ARGS((struct convection *cp,char *cptr));
static void process_question __ARGS((int channel,char type,char *name,char *cptr));
static void question_command __ARGS((struct convection *cp,char *cptr));
static void log_command __ARGS((struct convection *cp,char *cptr));
static void nonet_command __ARGS((struct convection *cp));
static void gpassword_command __ARGS((struct convection *cp,char *cptr));
static void gprivate_command __ARGS((struct convection *cp,char *cptr));
static void group_command __ARGS((struct convection *cp,char *cptr));
static int conv_rand __ARGS((void));
static void conv_randomize __ARGS((void));
static int conv_random __ARGS((int num, int base));
static void smiley_command __ARGS((struct convection *cp));
#ifdef SAMCALLB
static void call_command __ARGS((struct convection *cp));
#endif
static void roll_command __ARGS((struct convection *cp));
static void cut_command __ARGS((struct convection *cp));
static void glog_command __ARGS((struct convection *cp));
static void net_command __ARGS((struct convection *cp));
static void list_groups_command __ARGS((struct convection *cp));
static void join_command __ARGS((struct convection *cp));
static int gatekeeper __ARGS((struct convection *cp,int channel));
static void process_commands __ARGS((struct convection *cp,struct mbx *m));
void conv_incom __ARGS((int s,void *t,void *p));
void conversWriteall __ARGS((char *str));
static void convcolorchange __ARGS((struct convection *p,unsigned char *str));
static void cmdsummary_command __ARGS((struct convection *cp));
#ifdef UNIX
long sendfile __ARGS((FILE *fp,int s,int mode,int hash));
int DisplayFile __ARGS((char *fname,int socket));
#endif
int sockblock __ARGS((int s, int value));
static void conv_usflush __ARGS((int s));


#undef CNAMELEN
#define CNAMELEN 16
char Chostname[CNAMELEN+1], CConsole[CNAMELEN+1];

char *confMOTD = NULL;

struct group	{
	int		channel;	/* channel # for this group info */
	char 		name[34];	/* group's name */
	char		password[NAMELEN+1];	/* password required to enter group */
	char		moderator[NAMELEN+1];	/* real name of current moderator */
	FILE		*qfile;		/* stream of current question file */
	short		nextq;		/* last question number in qfile */
	short		totalq;		/* total questions in qfile */
	FILE		*logfile;	/* stream of current logfile */
	char            logged;         /* status of log file (1=open) */
	char		private;	/* is it private (1 = yes) */
	struct group 	*next;		/* next group in queue */
};

#define NULLGROUP ((struct group *) 0)
#define NOCONTROL ((struct group *) -1)

static char nonetoverride;
static struct group *groups;
static struct group * find_group __ARGS((int channel));
static void bye_command __ARGS((struct convection *cp));
static void nonet_command __ARGS((struct convection *cp));
static void process_question __ARGS((int channel, char type, char *name, char *cptr));
static void glog_command __ARGS((struct convection *cp));
static void update_net_data __ARGS((struct group *gp));
static struct group *find_group __ARGS((int channel));
static struct group *get_group __ARGS((struct convection *cp));
static struct group *can_gcontrol __ARGS((struct convection *cp));
static struct group *lookup_group __ARGS((char *name));

extern char *confMOTD;
extern char Ccall[AXALEN], Calias[AXALEN];


static int docfilter __ARGS((int argc,char *argv[],void *p));
static int doconvconsole __ARGS((int argc,char *argv[],void *p));
static int dochostname __ARGS((int argc,char *argv[],void *p));
static int dociface __ARGS((int argc,char *argv[],void *p));
static int doconfcall __ARGS((int argc,char *argv[],void *p));
static int doconfalias __ARGS((int argc,char *argv[],void *p));
static int doclink __ARGS((int argc,char *argv[],void *p));
static int doct4 __ARGS((int argc,char *argv[],void *p));
static int doentrychannel __ARGS((int argc,char *argv[],void *p));
static int doconfmotd __ARGS((int argc,char *argv[],void *p));
static int doconvnet0 __ARGS((int argc,char *argv[],void *p));
static int dotimestamp __ARGS((int argc,char *argv[],void *p));
static int docmaxwait __ARGS((int argc,char *argv[],void *p));
static int dohmaxq __ARGS((int argc,char *argv[],void *p));
static int doumaxq __ARGS((int argc,char *argv[],void *p));
static int doheader __ARGS((int argc,char *argv[],void *p));
int doconvers __ARGS((int argc,char *argv[],void *p));

static struct cmds DFAR Ccmds[] = {
#ifdef AX25
    "alias",	doconfalias,0, 0, NULLCHAR,
#endif
    "console",	doconvconsole, 0, 0, NULLCHAR,
    "entrychannel", doentrychannel, 0, 0, NULLCHAR,
    "filter",    docfilter,   0, 0, NULLCHAR,
    "hmaxq",    dohmaxq,    0, 0, NULLCHAR,
    "header",   doheader,   0, 0, NULLCHAR,
    "hostname", dochostname,0, 0, NULLCHAR,
    "interface",dociface,   0, 0, NULLCHAR,
#ifdef LINK
    "link",     doclink,    0, 0, NULLCHAR,
#endif
    "maxwait",  docmaxwait, 0, 0, NULLCHAR,
#ifdef AX25
    "motd",	doconfmotd, 0, 0, NULLCHAR,
    "mycall",   doconfcall, 0, 0, NULLCHAR,
#endif
    "net0",	doconvnet0, 0, 0, NULLCHAR,
#ifdef AX25
    "t4",       doct4,      0, 0, NULLCHAR,
    "timestamp",dotimestamp,0, 0, NULLCHAR,
#endif
    "umaxq",    doumaxq,    0, 0, NULLCHAR,
    NULLCHAR,
};

/* Multiplexer for top-level convers command */
int
doconvers(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return subcmd(Ccmds,argc,argv,p);
}

#ifdef AX25
/* Display or change our AX.25 conference call */
static int
doconfcall(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    char tmp[AXBUF];

	if(argc < 2){
        tprintf(fullstrcr,pax25(tmp,Ccall));
		return 0;
	}
    if(setcall(Ccall,argv[1]) == -1)
		return -1;
    return 0;
}

/* Display or change our AX.25 conference alias */
static int
doconfalias(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    char tmp[AXBUF];

	if(argc < 2){
        tprintf(fullstrcr,pax25(tmp,Calias));
		return 0;
	}
    if(setcall(Calias,argv[1]) == -1)
		return -1;
    return 0;
}

int32 CT4init = 7200;   /* 2 hours default */

/* Set link redundancy timer */
static int
doct4(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setlong(&CT4init,redun,argc,argv);
}
#endif /* AX25 */


int CChannel = 0;	/* default entry to channel 0 */

/* Set entry channel number */
static int
doentrychannel(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setint(&CChannel,entrystr,argc,argv);
}


/* Allow nets to be held on channel 0 */
static int
doconvnet0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setbool(&ConvNet0,allownets,argc,argv);
}


/* Give login header */
static int
doheader(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setbool(&ConvHeader,givehead,argc,argv);
}


/* Allow broadcasts to be timestamped */
static int
dotimestamp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setbool(&TimeStamp,timestampstr,argc,argv);
}


#ifdef UNIXJNOS
int DisplayFile (char *fname,int socket)
{
FILE *fp;
int size = 0;

	if((fp = fopen(fname,READ_TEXT)) != (FILE *)0) {
		sendfile(fp,socket,ASCII_TYPE,0);
		size = ftell (fp);
		fclose(fp);
		}
	return (size);
}


/* Set interface (or AX25) flags on 1 or all interfaces */
int
dosetflag(int argc,char *argv[],void *p,int flagtoset, int AX25only)
{
struct iface *ifp;

	if(argc < 2) {
		for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next)
			if(ifp->flags & flagtoset)
		tprintf("%s\n",ifp->name);
	} else {
		if((argc == 3) && (strcmp(argv[1],"all") == 0)) {
			for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next)
				if(!AX25only || ifp->type == CL_AX25) {
					if(strcmp(argv[2],"on") == 0)
						ifp->flags |= flagtoset;
					else if(strcmp(argv[2],"off") == 0)
						ifp->flags &= ~flagtoset;
					else {
						tprintf("Please use on or off.\n");
						return 0;
					}
				}
			return 0;
		} else if((ifp = if_lookup(argv[1])) == NULLIF){
			tprintf(Badinterface,argv[1]);
			return 1;
		}
		if(AX25only && ifp->type != CL_AX25) {
			tprintf("not an AX.25 interface\n");
			return -1;
		}
		ifp->flags ^= flagtoset;
	}
	return 0;
}
#endif


static int
dociface(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return(dosetflag(argc,argv,p,IS_CONV_IFACE, 1));
}

int32 CMaxwait = MAX_WAITTIME;

/* Set maxwait time for timed out links */
static int
docmaxwait(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setlong(&CMaxwait,maxwaitstr,argc,argv);
}

int HMaxQ = 6*1024L;

/* Set max qlimit for host links */
static int
dohmaxq(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setint(&HMaxQ,hostqueuestr,argc,argv);
}

int UMaxQ = 3*1024L;

/* Set max qlimit for user links */
static int
doumaxq(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setint(&UMaxQ,userqueuestr,argc,argv);
}

static int
dochostname(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    if(argc == 1)
        tprintf(fullstrcr,Chostname);
    else {
        strncpy(Chostname,argv[1],NAMELEN);
        Chostname[NAMELEN] = '\0';
    }
    return 0;
}


static int
doconvconsole(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    if(argc == 1)
        tprintf(fullstrcr,CConsole);
    else {
        strncpy(CConsole,argv[1],NAMELEN);
        CConsole[NAMELEN] = '\0';
    }
    return 0;
}


/*
** View/Change the message we send to new conference connects.
*/

static int doconfmotd(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if (argc < 2)
		tprintf(motdstr,confMOTD);
	else
	{
		if (confMOTD != NULL) free(confMOTD);
		confMOTD = malloc(strlen(argv[1]) + 1);
		strcpy(confMOTD,argv[1]);
	}
	return 0;
}

static int
docfilter(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    int32 addr;
    struct filter_link *fl;

    if(argc == 1) {
        if(Filterlinks) {
            tprintf(modeis, FilterMode ? acceptstr : refuse);
            for(fl=Filterlinks;fl;fl=fl->next)
                tprintf(fullstrcr,inet_ntoa(fl->addr));
        }
        return 0;
    }
    if(!stricmp(argv[1],"mode")) {
        if(argc == 2)
            tprintf(modeis, FilterMode ? acceptstr : refuse);
        else {
            if(*argv[2] == 'a' || *argv[2] == 'A')
                FilterMode = 1;
            else
                FilterMode = 0;
        }
        return 0;
    }
    if((addr = resolve(argv[1])) == 0) {
        tprintf(Badhost,argv[1]);
        return 1;
    }
    /* check to see if we already have this in the list */
    for(fl=Filterlinks;fl;fl=fl->next)
        if(fl->addr == addr)
            return 0;       /* already have this one ! */

    /* Seems like a new one */
    fl = (struct filter_link *)callocw(1,sizeof(struct filter_link));
    fl->addr = addr;
    fl->next = Filterlinks;
    Filterlinks = fl;
    return 0;
}

#ifdef LINK
static int
doclink(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    int32 addr;
    struct permlink *pl;

    if(argc == 1) {
        for(pl=permlinks;pl;pl=pl->next)
            tprintf(fullstrcr,inet_ntoa(pl->addr));
        return 0;
    }
    if((addr = resolve(argv[1])) == 0) {
        tprintf(Badhost,argv[1]);
        return 1;
    }
    /* check to see if we already have a link to such animal,
     * this happens when we stop and restart the server - WG7J
     */
    for(pl=permlinks;pl;pl=pl->next)
        if(pl->addr == addr)
            return 1;       /* already have this one ! */

    /* Seems like a new link ! Go add it */
    pl = (struct permlink *)callocw(1,sizeof(struct permlink ));
    pl->addr = addr;
    pl->next = permlinks;
    permlinks = pl;
    if(argc > 1) {
        strncpy(pl->name,argv[2],NAMELEN);
        update_permlinks(pl->name,NULLCONNECTION);
	pl->retrytime -= 55;	/* 1st start in 5 seconds */
	pl->waittime = 30;	/* 2nd after 1 minute */
        
    }
    if(!Linker)
        Linker = newproc("Clinker",1024,connect_permlinks,0,0,NULL,0);

    return 0;
}
#endif


#ifdef UNIXJNOS
int
deleteserver (int *mysocket)
{
	if (*mysocket != -1)	{
		close_s(*mysocket);
		*mysocket = -1;
	} else
		tprintf ("The server is not active - nothing to stop!\n");
	return 0;
}


/* Start up a TCP server */
int
installserver(int argc,char *argv[],int *mysocket,char *listener,int defport,
	char *procname,void (*proc)__ARGS((int,void *,void *)),int size,void *parm)
{
struct sockaddr_in lsocket;
int s;

	if(*mysocket != -1){
		return 0;
	}
	psignal(Curproc,0); 	/* Don't keep the parser waiting */
	chname(Curproc,listener);

	lsocket.sin_family = AF_INET;
	lsocket.sin_addr.s_addr = INADDR_ANY;
	if(argc < 2)
		lsocket.sin_port = defport;
	else
		lsocket.sin_port = atoi(argv[1]);
	*mysocket = socket(AF_INET,SOCK_STREAM,0);
	bind(*mysocket,(char *)&lsocket,sizeof(lsocket));
	listen(*mysocket,1);
	for(;;){
		if((s = accept(*mysocket,NULLCHAR,(int *)NULL)) == -1)
			break;	/* Service is shutting down */

        /* Low mem check now done in tcpin.c - WG7J */
        /* Spawn a server */
        newproc(procname,(unsigned int)size,proc,s,parm,NULL,0);
	}
	return 0;
}
#endif


/* Stop convers server */
int
conv0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (deleteserver (&Sconv));
}

/* Start up convers server */
int
conv1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if (!Chostname[0])
		strncpy (Chostname, Hostname, CNAMELEN);
	installserver (argc, argv, &Sconv, "Conference listener", IPPORT_CONVERS,
		conversd, conv_incom, 1024, NULL);
#ifdef LINK
	if (Linker)
		killproc (Linker);
#endif
	return 0;
}

static void
free_connection(cp)
register struct convection *cp;
{
register struct permlink *p;

	for (p = permlinks; p; p = p->next)
	        if (p->convection == cp)
			p->convection = NULLCONNECTION;
	free(cp->ibuf);
        if(cp->flags & CLOSE_SOCK)
	        close_s(cp->fd);
	free((char *) cp);
}

static void
free_closed_connections()
{
register struct convection *cp,*p;
time_t currtime;

	currtime = time(&currtime);

	for(p = NULLCONNECTION,cp = convections; cp; )
		if(cp->type == CT_CLOSED ||
			(cp->type == CT_UNKNOWN && cp->time + 300 < currtime)) {
			if(p) {
				p->next = cp->next;
				free_connection(cp);
				cp = p->next;
			} else {
				convections = cp->next;
				free_connection(cp);
				cp = convections;
			}
		} else {
			p = cp;
			cp = cp->next;
		}
}

static void
update_permlinks(name,cp)
char  *name;
struct convection *cp;
{
register struct permlink *p;
time_t currtime;

	for(p = permlinks; p; p = p->next)
		if(!strcmp(p->name,name)) {
			currtime = time(&currtime);
			p->convection = cp;
			p->statetime = currtime;
			p->tries = 0;
			p->waittime = 60;
			p->retrytime = currtime + p->waittime;
		}
}

static struct convection *
alloc_connection(fd)
int  fd;
{
register struct convection *cp;
time_t currtime;

	currtime = time(NULL);

	cp = (struct convection *)callocw(1,sizeof(struct convection ));
	cp->ibuf = (char *)callocw(1,LINELEN);
	cp->fd = fd;
	cp->maxq = UMaxQ;       /* Maximum qlimit for user */
        cp->flags = CLOSE_SOCK+USE_SOUND;       /* close on exit, by default */
	cp->time = currtime;
	cp->next = convections;
	cp->paged = -1;
	cp->net = -1;
	cp->features = 0;
	convections = cp;
	return cp;
}

#ifdef LINK
/* check the host links for backlogged data.
 * If larger then set threshold, kill the link.
 * WG7J, 930208
 */
void check_buffer_overload(void) {
    struct convection *p;

    /* check the size of the outstanding data buffers */
    for(p = convections; p; p = p->next)
        if((p->maxq != 0) && (socklen(p->fd,1) > p->maxq)) {
                /* Blow this one out of the water */
                shutdown(p->fd,2);
                close_s(p->fd);
            }
}

void
connect_permlinks(a,b,c)
int a;
void *b;
void *c;
{
    int s;
	register struct permlink *p;
	struct sockaddr_in cport;
	time_t currtime;

	for(;;) {
        mspause(15000L);
		for(p = permlinks; p; p = p->next) {
			currtime = time(&currtime);
			if(p->convection || p->retrytime > currtime)
				continue;
			p->tries++;
			p->waittime <<= 1;
	                if(p->waittime > CMaxwait)
            		    p->waittime = CMaxwait;
			p->retrytime = p->waittime + currtime;
            cport.sin_family = AF_INET;
            cport.sin_port = IPPORT_CONVERS;
            cport.sin_addr.s_addr = p->addr; /* we've resolved this earlier */
            if((s = socket(AF_INET,SOCK_STREAM,0)) == -1)
                continue;
            if(connect(s,(char *)&cport,SOCKSIZE) == -1) {
                shutdown(s,2);  /* to make sure it doesn't linger around */
                close_s(s);     /* WG7J - 9207228 */
                continue;
            }
			p->fd = s;
            if(newproc("permlink",2048,conv_incom,s,0,NULL,0) == NULLPROC){
                shutdown(s,2);  /* blow it out of the water :-) */
                close_s(s);
            }
		}
        check_buffer_overload();
	}
}
#endif


static void
clear_locks()
{
register struct convection *p;

	for (p = convections; p; p = p->next)
		p->locked = 0;
}

static void send_sounds(p)
struct convection *p;
{
    if(p->flags & USE_SOUND)
        p->xmitted += usputs(p->fd,"");
}

extern char *Months[];		/* in smtpserv.c */

static char *
timestring(gmt)
long  gmt;
{
static char  buffer[10];
struct tm *tm;
time_t currtime;

	currtime = time(&currtime);


	tm = localtime(&gmt);
	if(gmt + 24 * 60 * 60 > currtime)
		sprintf(buffer," %02d:%02d",tm->tm_hour,tm->tm_min);
	else
		sprintf(buffer,"%-3.3s %2d",Months[tm->tm_mon + 1],tm->tm_mday);
	return buffer;
}

static void
send_user_change_msg(name,host,mytime,oldchannel,newchannel,pers)
char  *name,*host;
long mytime;
int  oldchannel,newchannel;
char *pers;
{
register struct convection *p;
time_t currtime;
register struct group *gold, *gnew;

	currtime = time(&currtime);

	gold = find_group (oldchannel);
	gnew = find_group (newchannel);
	if(gold && gold->logfile) 	{
		if(newchannel >= 0)
			fprintf(gold->logfile, theyswitched, stars,
			     name,newchannel, timestring (currtime));
		else
			fprintf(gold->logfile,theysigned, stars, name, ff_str, timestring (currtime));
		}
	if (gnew && gnew->logfile)
		fprintf(gnew->logfile,theysigned, stars, name, n_str, timestring (currtime));

	for(p = convections; p; p = p->next) {
		pwait(NULL);
		if(p->type == CT_USER && !p->via && !p->locked) {
			if ((newchannel == oldchannel) && (newchannel != -1)) {
				if (p->channel == newchannel) {
					if ((*pers != '\0') && (strcmp(pers, "@")))
						p->xmitted += usprintf(p->fd, "*** (%s) %s@%s on channel %d set personal text:\n    %s\n", timestring(currtime), name, host, newchannel, pers);
					else
						p->xmitted += usprintf(p->fd, "*** (%s) %s@%s on channel %d removed personal text.\n", timestring(currtime), name, host, newchannel);
					p->locked = 1;
				}
			} else {
				if(p->channel == oldchannel) {
					convcolorchange (p, SYSCOLORS);
					if(newchannel >= 0)
						p->xmitted += usprintf(p->fd, theyswitched, stars,
						     name,newchannel, timestring (currtime));
					else
						p->xmitted += usprintf(p->fd,
							theysigned, stars, name, ff_str, timestring (currtime));
					p->locked = 1;
				}
				if(p->channel == newchannel) {
					convcolorchange (p, SYSCOLORS);
					send_sounds (p);
					p->xmitted += usprintf(p->fd,
						theysigned, stars, name, n_str, timestring (currtime));
					p->locked = 1;
				}
			}
		}
		if(p->type == CT_HOST && !p->locked) {
			p->xmitted += usprintf(p->fd,huserstr,name,host,mytime,oldchannel,newchannel,pers);
			p->locked = 1;
		}
	}
}

static char *
formatline(prefix,text)
char  *prefix,*text;
{

#define PREFIXLEN 10
#define CONVLINELEN   79

register char  *f,*t,*x;
register int  l,lw;
static char buf[2*LINELEN];

	for(f = prefix,t = buf; *f; *t++ = *f++) ;
	l = (int)(t - buf);
	f = text;
	*t++ = ' ';
	l++;

	for(;;) {
		while(isspace(uchar(*f)))	{
#ifndef NOSPACES
			*t++ = *f;
			l++;
#endif
			f++;
			}
		if(!*f)		{
			*t++ = '\n';
			*t = '\0';
			return buf;
			}
		for (x = f; *x && !isspace(uchar(*x)); x++) ;
		lw = (int)(x - f);
		if(l > PREFIXLEN && l + 1 + lw > CONVLINELEN)	{
			*t++ = '\n';
			l = 0;
			}
#ifndef NOSPACES
		while(l < PREFIXLEN)	{
#else
		do {
#endif
			*t++ = ' ';
			l++;
#ifndef NOSPACES
		}
#else
		} while(l < PREFIXLEN);
#endif
		while(lw--) {
			*t++ = *f++;
			l++;
		}
	}
}

static void
send_msg_to_user(fromname,toname,text)
char  *fromname,*toname,*text;
{
register struct convection *p;

	for(p = convections; p; p = p->next) 		{
		pwait(NULL);
		if(p->type == CT_USER && (!strcmpi(p->name,toname) || !strcmpi(p->nickname, toname)))
			if(p->via) 	{
				if(!p->via->locked) 	{
					p->via->xmitted += usprintf(p->via->fd,
						umsgstr,fromname,toname,text);
					p->via->locked = 1;
					}
			} else 		{
				if(!p->locked) 		{
					if(strcmp(fromname,conversd)) 	{
						char buffer[14+NAMELEN];
						time_t currtime;
						currtime = time(&currtime);
						convcolorchange (p, INFOCOLORS);
						sprintf(buffer,(TimeStamp) ? timestampfmt : nontimestampfmt,fromname, timestring(currtime));
						p->xmitted += usprintf(p->fd,fullstr,formatline(buffer,text));
					} else	{
						convcolorchange (p, SYSCOLORS);
						p->xmitted += usprintf(p->fd,fullstrcr,text);
					}
					p->locked = 1;
					}
			}
		}
}

static void
send_msg_to_channel(fromname,channel,text)
char  *fromname;
int  channel;
char  *text;
{
char  buffer[7+NAMELEN];
register struct convection *p;
register struct group *gp;
time_t currtime;

	currtime = time(&currtime);
	sprintf(buffer,(TimeStamp) ? timestampfmt2 : nontimestampfmt2,fromname, timestring(currtime));
	gp = find_group (channel);
	if (gp && gp->logfile)
		fprintf(gp->logfile,fullstr,(!strcmp (fromname,conversd)) ? text : formatline(buffer,text));

	for(p = convections; p; p = p->next) {
		pwait(NULL);
		if(p->type == CT_USER && p->channel == channel)
			if(p->via) {
				if(!p->via->locked) {
					p->via->xmitted += usprintf(p->via->fd,
					cmsgstr,fromname,channel,text);
					p->via->locked = 1;
				}
			} else {
				if(!p->locked) {
					convcolorchange (p, TEXTCOLORS);
					p->xmitted += usprintf(p->fd,fullstr,
						(!strcmp (fromname,conversd)) ? text : formatline(buffer,text));
					if (!strcmp (fromname,conversd) && !strchr (text, '\n'))
						p->xmitted += usprintf(p->fd,"\n");
					p->locked = 1;
					usflush (p->fd);
				}
			}
	}
}


static void
send_invite_msg(fromname,toname,channel, msg)
char  *fromname,*toname;
int  channel;
char  *msg;
{
char buffer[80];
struct convection *p;
#ifdef MAILBOX
int i, yep = 0;
struct mbx *m = 0;
#endif
time_t currtime;

	currtime = time(&currtime);

#ifdef MAILBOX
	if (!yep)	{
#ifdef UNIXJNOS
		m = Mbox;
		for(i = 0; i < NUMMBX; m++,i++){
		        if(m != NULLMBX){
#else
		for(i = 0; i < NUMMBX; i++){
		        if((m = Mbox[i]) != NULLMBX){
#endif
		            if(m->state == MBX_CMD && !stricmp(m->name,toname)) {
		                (void) usprintf(m->user,mbinvitetext,amessage,fromname,timestring(currtime),stars,channel,channel);
				if (msg[0])
					usprintf (m->user, msgtext, fromname, msg);
                		usflush(m->user);
		                clear_locks();
		                sprintf(buffer,responsetext,stars,toname,"BBS@");
                		strcat(buffer,Hostname);
		                send_msg_to_user(conversd,fromname,buffer);
                		return;
                	    }
            }
        }
    }
#endif

    /* check the current convers users */
    for(p = convections; p; p = p->next) {
		if(p->type == CT_USER && !strcmpi(p->name,toname)) {
			if(p->channel == channel) {
                clear_locks();
                sprintf(buffer,alreadyon,stars,userstr,toname);
                send_msg_to_user(conversd,fromname,buffer);
                return;
            }
			if(!p->via && !p->locked) {
				convcolorchange (p, INFOCOLORS);
				p->paged = channel;
        		        p->xmitted += usprintf(p->fd,invitetext,amessage,fromname, \
                                        timestring(currtime),stars,channel);
				if (msg[0])
					p->xmitted += usprintf (p->fd, msgtext, fromname, msg);
                		clear_locks();
		                sprintf(buffer,responsetext,stars,toname,Chostname);
                		send_msg_to_user(conversd,fromname,buffer);
		                return;
            			}	
			if(p->via && !p->via->locked) {
				p->via->xmitted += usprintf(p->via->fd, hinvi,fromname,toname,channel, msg);
				return;
			}
        }
	}
    /* Nothing found locally, invite user on all links */
	for(p = convections; p; p = p->next) {
		if(p->type == CT_HOST && !p->locked) {
            p->xmitted += usprintf(p->fd,hinvi,fromname,toname,channel, msg);
		}
	}
}



static void
time_command (cp)
struct  convection *cp;
{
time_t currtime;
char buff[24];

	currtime = time(&currtime);
	sprintf (buff, "%s%s is %s\n", stars,tmstr, timestring(currtime));
	clear_locks ();
	send_msg_to_channel(conversd,cp->channel,buff);
}


static int
onchannel (channel)
int channel;
{
int cnt = 0;
struct convection *p;

	for(p = convections; p; p = p->next)
		if(p->type == CT_USER && channel == p->channel)
			cnt++;
	return (cnt);
}

static void
bye_command(cp)
struct convection *cp;
{
register struct convection *p;
	
	if (cp->net != -1)	{
		int old = cp->channel;
		cp->channel = cp->net;
		nonet_command (cp);
		cp->channel = old;
	}
	switch(cp->type) {
	case CT_UNKNOWN:
		cp->type = CT_CLOSED;
		break;
	case CT_USER:
		cp->type = CT_CLOSED;
		convcolorchange (cp, SYSCOLORS);
		cp->xmitted += usprintf(cp->fd, exitingstr,conferencestr, Chostname);
		clear_locks();
		send_user_change_msg(cp->name,cp->host,cp->time,cp->channel,-1,cp->data);
		save_personal (cp);
		break;
	case CT_HOST:
		cp->type = CT_CLOSED;
		update_permlinks(cp->name,NULLCONNECTION);
		for (p = convections; p; p = p->next)
			if(p->via == cp) 	{
				p->type = CT_CLOSED;
				clear_locks();
				send_user_change_msg(p->name,p->host,p->time,p->channel,-1,p->data);
				}
		break;
    case CT_CLOSED:
		break;
    }
}

static void
mystatus(cp, old)
struct convection *cp;
int old;
{
int  now;
struct group *gp;

	now = onchannel (cp->channel);
	cp->xmitted += usprintf(cp->fd,nowchannel,stars, channelstr, cp->channel, now, (now > 1) ? "s" : empty);
	gp = find_group (cp->channel);
	if (gp != NULLGROUP)	{
		convcolorchange (cp, SYSCOLORS);
		cp->xmitted += usprintf(cp->fd, welcome, stars, (gp->private) ? privatestr : empty,
			(gp->private) ? " " : empty, gp->name, netcontrolstr, gp->moderator, cp->nickname);
	}
	send_user_change_msg(cp->name,cp->host,cp->time,old,cp->channel,cp->data);
}

static void
channel_command(cp)
struct convection *cp;
{
char  s[7];
int  newchannel, oldchannel;

	s[0] = '\0';
	sscanf(cp->ibuf,"%*s %6s",s);
	if(s[0] == '\0') 	{
		convcolorchange (cp, SYSCOLORS);
		cp->xmitted += usprintf(cp->fd,youare,stars,empty,cp->channel);
		return;
		}
	newchannel = atoi(s);
/*	if(newchannel < 0 || newchannel > MAXCHANNEL) {		*/
	if(newchannel < 0) 	{
		convcolorchange (cp, SYSCOLORS);
	        cp->xmitted += usprintf(cp->fd,cnumber,MAXCHANNEL);
        	return;
		}
	if(newchannel == cp->channel) 		{
		convcolorchange (cp, SYSCOLORS);
	        cp->xmitted += usprintf(cp->fd,youare,stars,"already ",cp->channel);
		return;
		}
	if (gatekeeper (cp, newchannel))
		return;
	oldchannel = cp->channel;
	cp->channel = newchannel;
	cp->nettime = time (&cp->nettime);
	cp->locked = 1;		/* for bump command	*/
	mystatus (cp, oldchannel);
}


static void
accept_command(cp)
struct convection *cp;
{
	if (cp->paged == -1)	{
		convcolorchange (cp, SYSCOLORS);
		cp->xmitted += usprintf (cp->fd, "??? %s\n", channelstr);
	} else		{
		sprintf (cp->ibuf, "/c %d", cp->paged);
		channel_command (cp);
	}
}


static char *
skipone (cptr, times)
char *cptr;
int times;
{
int k;
	k = strlen (cptr) - 1;
#ifndef TNOS_68K
	if (cptr[k] == '\n')	{
#else
	if (cptr[k] == '\l')	{
#endif
		cptr[k] = 0;
		if (cptr[k - 1] == '\r')
			cptr[k - 1] = 0;
	}
	do	{
		while (*cptr && *cptr != ' ')
			cptr++;
		while (*cptr && *cptr == ' ')
			cptr++;
	} while (--times);
	return (cptr);
}

extern char *ConfNews;

static void
help_command(cp)
struct convection *cp;
{
int k;
char *cptr;
char *file2open = 0;

	sockblock (cp->fd, SOCK_BLOCK);
	cptr = cp->ibuf;
	cptr = skipone (cptr, 1);

	file2open = (tolower (*cptr) != 'n') ? ConfHlp : (tolower(cptr[2]) == 'w') ? ConfNews: NetControlHlp;
	convcolorchange (cp, INFOCOLORS);
	k = DisplayFile (file2open,cp->fd);
	if (k)
		cp->xmitted += k;
	else	{
		convcolorchange (cp, SYSCOLORS);
		cp->xmitted += usprintf(cp->fd,noopenstr, file2open);
	}
	cp->xmitted += usprintf(cp->fd, trailer);	
	sockblock (cp->fd, SOCK_NOTXBLOCK);
}

static void
news_command(cp)
struct convection *cp;
{
	strcpy (cp->ibuf, "h news");
	help_command (cp);
}


static void
invite_command(cp)
struct convection *cp;
{
struct group *gp;
char toname[10], chk[4], *cptr, *cp2;
	
	chk[0] = toname[0] = '\0';
	sscanf(cp->ibuf,"%*s %16s %3s",toname, chk);
	cptr = chk;
	if (chk[0])	{
		cptr = cp->ibuf;
		cptr = skipone (cptr, 2);
		cptr[51] = 0;
		}
	if(*toname)		{
		if ((cp2 = strchr (toname, '@')) != NULLCHAR)
			cp2 = '\0';
		gp = find_group (cp->channel);
		if (gp && gp->private && cp->net != cp->channel)	{
			convcolorchange (cp, SYSCOLORS);
			cp->xmitted += usprintf (cp->fd, noinvite, stars,privatestr, netcontrolstr);
		} else
			send_invite_msg(cp->name,toname,cp->channel, cptr);
		}
}

#ifdef UNIX
int ShowConfLinks(int s, int full) {
    int num;
    struct convection *pc;
    struct permlink *pp;
    char  tmp[20];

    num = usprintf(s,"Host       State         Since%s\n",
                (full) ? " NextTry Tries Receivd Xmitted" : "");
    for(pc = convections; pc; pc = pc->next)	{
	pwait(NULL);
        if(pc->type == CT_HOST)
            num += usprintf(s,
                (full) ?
                "%-10s Connected    %s            %7d %7d\n" :
                "%-10s Connected    %s\n",
                pc->name,
                timestring(pc->time),
                pc->received,
                pc->xmitted);
    }

    for(pp = permlinks; pp; pp = pp->next)	{
	pwait(NULL);
        if(!pp->convection || pp->convection->type != CT_HOST) {
            strcpy(tmp,timestring(pp->retrytime));
            num += usprintf(s,
                (full) ?
                "%-10s %-12s %s %s %5d\n" :
                "%-10s %-12s %s\n",
                pp->name,
                pp->convection ? "Connecting" : "Disconnected",
                timestring(pp->statetime),
                tmp,
                pp->tries);
        }
    }
    num += usprintf(s,"***\n");
    return num;
}

static void
#ifdef PROTOTYPES
links_command(struct convection *cp)
#else
links_command(cp)
struct convection *cp;
#endif
{
	char full[3];
    int f = 0;

	full[0] = '\0';
	sscanf(cp->ibuf,"%*s %2s",full);
    if(*full == 'l' || *full == 'L')
        f = 1;
    cp->xmitted += ShowConfLinks(cp->fd,f);
    return;
}

#else

static void
links_command(cp)
struct convection *cp;
{
char  tmp[20];
char full[3];
struct convection *pc;
struct permlink *pp;

	full[0] = '\0';
	convcolorchange (cp, INFOCOLORS);

	sscanf(cp->ibuf,"%*s %2s",full);
	cp->xmitted += usprintf(cp->fd,linkhdr,	(full[0] != 'l') ? empty : linkhdr2);
	for(pc = convections; pc; pc = pc->next)	{
		pwait(NULL);
		if(pc->type == CT_HOST) {
			cp->xmitted += usprintf(cp->fd,
				(*full == 'l') ?
                "%-16s %-12s  %s     %5d %7d %7d\n" :
                "%-16s %-12s  %s\n",
				pc->name,
				"Connected",
				timestring(pc->time),
				0,	/* always 0, cuz NOS handles the tx-queue */
				pc->received,
				pc->xmitted);
		}
	}
	for(pp = permlinks; pp; pp = pp->next)	{
		pwait(NULL);
		if(!pp->convection || pp->convection->type != CT_HOST) {
			strcpy(tmp,timestring(pp->retrytime)),
				cp->xmitted += usprintf(cp->fd,
				(*full == 'l') ?
                "%-16s %-12s  %s   %s %5d\n" :
                "%-16s %-12s  %s\n",
				pp->name,
				pp->convection ? "Connecting" : "Disconnected",
				timestring(pp->statetime),
				tmp,
				pp->tries);
		}
	}
	cp->xmitted += usprintf(cp->fd,trailer);
}
#endif


static void
msg_command(cp)
struct convection *cp;
{
char toname[NAMELEN+1],*text;
register struct convection *p;

	toname[0] = '\0';
	sscanf(cp->ibuf,"%*s %16s",toname);
	text = &cp->ibuf[0];
	text = skipone (text, 2);

	if(!*text)
		return;
	for(p = convections; p; p = p->next)
		if(p->type == CT_USER && (!strcmpi(p->name,toname) || !strcmpi(p->nickname,toname)))	{
			send_msg_to_user(cp->nickname,toname,text);
			return;
			}
	convcolorchange (cp, SYSCOLORS);
	cp->xmitted += usprintf(cp->fd,nosuchuser,stars,toname);
}

static void
announce_new_user (cp)
struct convection *cp;
{
char dummy[80];
register struct group *gp;
int numgroups;

	if (CountConfUsers() > 1)	{
		cp->xmitted += usprintf (cp->fd, online, stars, CountConfUsers());
		if ((numgroups = CountConfGroups()) != 0)
			cp->xmitted += usprintf (cp->fd, groupsavail, stars, (numgroups > 1) ? "are" : "is", numgroups, (numgroups > 1) ? "s" : empty);
	}

	sprintf (dummy, newuserstr, stars, cp->name, cp->channel);
	for (gp = groups; gp; gp = gp->next)
		if (gp->channel != cp->channel)
			send_msg_to_user (conversd, gp->moderator, dummy);
	send_user_change_msg(cp->name,cp->host,cp->time,-1,cp->channel,cp->data);
}


static void
color_command(cp)
struct convection *cp;
{
char dummy[8];

	sscanf(cp->ibuf,"%*s %6s",dummy);
	if (dummy)	{
		if (!stricmp (dummy, "on"))
			cp->flags |= USE_COLOR;
		if (!stricmp (dummy, "off"))
			cp->flags &= ~USE_COLOR;
	}
	
	cp->colorset[0] = '\0';
	convcolorchange (cp, SYSCOLORS);
	cp->xmitted += usprintf(cp->fd,colorstatus, stars, (cp->flags & USE_COLOR) ? n_str : ff_str);
	cp->flags |= CHANGED_INFO;
}


/* save the personal information for this user */
void save_personal(cp)
struct convection *cp;
{
FILE *old, *new;
char *newname;
int found = 0;

	if ((!cp->nickname[0] && !cp->data) || !(cp->flags & CHANGED_INFO))
		return;
	if((old = fopen(ConfInfo,UPDATE_TEXT)) == NULL)
		if((old = fopen(ConfInfo,CREATE_TEXT)) == NULL)
			return;
	rewind (old);
	newname = (char *) malloc (strlen (ConfInfo) + 5);
	sprintf (newname, "%s.new", ConfInfo);
	if ((new = fopen(newname, CREATE_TEXT)) == NULLFILE)	{
		fclose (old);
		free (newname);
		return;
	}
	while (fgets(cp->ibuf,LINELEN,old) != NULLCHAR)	{
		pwait(NULL);
		rip (cp->ibuf);
		if (!stricmp (cp->ibuf, cp->name))	{
			found = 1;
			for ( ; ; )		{
				if (fgets(cp->ibuf, LINELEN, old) == NULLCHAR)
					break;
				if (cp->ibuf[0] != ' ')		{
/*					fputs (cp->ibuf, new);		*/
					break;
				}
			}
			break;
		}
		fprintf (new, fullstrcr, cp->ibuf);
	}
	fprintf (new, fullstrcr, cp->name);
	if (cp->nickname)
		fprintf (new, " nickname %s\n", cp->nickname);
	if (cp->data)
		fprintf (new, " personal %s\n", cp->data);
	if (cp->flags & USE_COLOR)
		fprintf (new, " color ON\n");
	if (found)		/* add rest of file */
		do	{
			fputs (cp->ibuf, new);
		} while (fgets(cp->ibuf,LINELEN,old) != NULLCHAR);
	fclose (new);
	fclose (old);
	unlink (ConfInfo);
	rename (newname, ConfInfo);
	free (newname);
	return;
}

/* find the personal information for this user */
void set_personal(cp)
struct convection *cp;
{
FILE *fp;

	if((fp = fopen(ConfInfo,READ_TEXT)) == NULL)
		return;
	while(fgets(cp->ibuf,LINELEN,fp) != NULL) {
		pwait(NULL);
		if (*(cp->ibuf) == ' ')
			continue;
		rip (cp->ibuf);
		if(stricmp(cp->name,cp->ibuf))
			continue;
		if (fgets(cp->ibuf,LINELEN,fp) != NULL) {
			if (!strnicmp (cp->ibuf, " nick", 5))	{
				/* Found nickname data ! */
				*(cp->ibuf) = '/';
				nickname_command(cp);
				fgets(cp->ibuf,LINELEN,fp);
			}
			if (!strnicmp (cp->ibuf, " pers", 5))	{
				/* Found personal data ! */
				*(cp->ibuf) = '/';
				personal_command(cp);
				fgets(cp->ibuf,LINELEN,fp);
			}
			if (!strnicmp (cp->ibuf, " color", 6))	{
				/* Found color data ! */
				*(cp->ibuf) = '/';
				color_command(cp);
			}
			cp->xmitted += usprintf(cp->fd,welcomeback, stars, cp->nickname);
		}
	}
	fclose(fp);
        return;
}

static void
name_command(cp)
struct convection *cp;
{
int  newchannel;
char dummy[8];

	cp->name[0] = dummy[0] = '\0';
	sscanf(cp->ibuf,"%*s %16s %6s",cp->name,dummy);
	newchannel = atoi(dummy);
	if(!*cp->name)
		return;
	strcpy (cp->nickname, cp->name);
	strlwr(cp->name);
	strcpy(cp->host,Chostname);
	cp->type = CT_USER;
	cp->channel = (newchannel == -1) ? CChannel : newchannel;

	announce_new_user (cp);
	set_personal(cp);
	cp->flags &= ~CHANGED_INFO;
	mystatus (cp, (int) -1);
}

#ifdef LZW
/* Set or show the status of the 'compressed' flag - KO4KS */
static void
compressed_command(cp)
struct convection *cp;
{
#ifndef UNIXJNOS 
char *cp2;
struct usock *up;

    if((cp2 = strchr(cp->ibuf,' ')) != NULLCHAR) {
        cp2++;
        if(*cp2 == 'o' || *cp2 == 'O') {   /* There is an argument */
            if(cp2[1] == 'f' || cp2[1] == 'F')    /* Turn it off */
                cp->flags &= ~USE_LZW;
            else
                cp->flags |= USE_LZW;
	    up = itop(cp->fd);
    	    if ((cp->flags & USE_LZW) || (up->zout != NULLLZW))
	    	togglelzw (cp->fd, cp->flags & USE_LZW);
            
        }
    }

    convcolorchange (cp, SYSCOLORS);
#endif 
    usprintf(cp->fd,"%sStream compression o%s\n", stars, (cp->flags & USE_LZW) ? n_str : ff_str);
    return;
}
#endif


/* Set or show the status of the 'sound' flag - WG7J */
static void
sounds_command(cp)
struct convection *cp;
{
    char *cp2;

    if((cp2 = strchr(cp->ibuf,' ')) != NULLCHAR) {
        cp2++;
        if(*cp2 == 'o' || *cp2 == 'O') {   /* There is an argument */
            if(cp2[1] == 'f' || cp2[1] == 'F')    /* Turn it off */
                cp->flags &= ~USE_SOUND;
            else
                cp->flags |= USE_SOUND;
        }
    }

    convcolorchange (cp, SYSCOLORS);
    usprintf(cp->fd,"%sSounds o%s\n", stars, (cp->flags & USE_SOUND) ? n_str : ff_str);
    return;
}

/* Send updated net data */
static void
update_net_data(gp)
struct group *gp;
{
struct convection *p;
        /* update all links */
        for(p=convections;p;p=p->next)	{
	    pwait(NULL);
            if(p->type == CT_HOST)
                p->xmitted += usprintf(p->fd,ndatstr,
                    gp->channel,gp->private,gp->logged,gp->nextq,gp->totalq,
		    gp->name,gp->moderator,gp->password);
	}
}


/* Send updated personal data, nickname */
static void
update_user_data(cp, personal)
struct convection *cp;
int personal;
{
struct convection *p;
        /* update all links */
        for(p=convections;p;p=p->next)	{
	    pwait(NULL);
            if(p->type == CT_HOST) 	{
		if (personal)
			send_user_change_msg(cp->name, cp->host, cp->time, cp->channel, cp->channel, cp->data);
		if (p->features & FEATURE_NICK)
	                p->xmitted += usprintf(p->fd,uaddstr,
        	            cp->name,cp->host,cp->nickname, cp->net, cp->data ? cp->data : "~~");
	    }
	}
}

/* Set some personal data, like name and qth - WG7J */
static void
personal_command(cp)
struct convection *cp;
{
char *cptr;

    cptr = cp->ibuf;
    cptr = skipone (cptr, 1);
    if(*cptr) {
        if(cp->data)
            free(cp->data);
        rip(cptr);      /* get rid of ending '\n' */
        cp->data = strdup(cptr);
        update_user_data (cp, 1);
	cp->flags |= CHANGED_INFO;
    }
    convcolorchange (cp, SYSCOLORS);
    cp->xmitted += usprintf(cp->fd,personalset, stars, cp->data ? cp->data : empty );
}



static void
nickname_command(cp)
struct convection *cp;
{
int nonick = 0, err = 0;

	convcolorchange (cp, SYSCOLORS);
	if (tolower(cp->ibuf[2]) == 'o')	{
		nonick = 1;
		strcpy (cp->nickname, cp->name);
	}
	else		{
		char new[16];
		new[0] = '\0';
		sscanf(cp->ibuf,string15, new);
		if (*new)
			if (strcmp (new, systemstr))
				strcpy (cp->nickname, new);
			else	{
				cp->xmitted += usprintf(cp->fd,	nicknamestr, stars, nicknmstr, "can't be ", new);
				err = 1;
			}
		}
	cp->xmitted += usprintf(cp->fd,	nicknamestr, stars, nicknmstr, (nonick) ? "re" : empty, cp->nickname);
	if (!err)
		update_user_data (cp, 0);
	cp->flags |= CHANGED_INFO;
}


static void
password_command(cp)
struct convection *cp;
{
char new[16];

	convcolorchange (cp, SYSCOLORS);
	if (tolower(cp->ibuf[2]) == 'o')	{
		cp->password[0] = '\0';
		cp->xmitted += usprintf(cp->fd, "%s %s!\n", passwordstr, clearedstr);
		return;
	}
	new[0] = '\0';
	sscanf(cp->ibuf,string15,new);
	if (*new)	{
		strcpy (cp->password, new);
		cp->xmitted += usprintf(cp->fd, "%s%s is set!\n", stars, passwordstr);
		}
	else		{
		if (cp->password[0])
			cp->xmitted += usprintf(cp->fd, "%s%s is '%s'.\n", stars,
				passwordstr, cp->password);
		else
			cp->xmitted += usprintf(cp->fd, "%sNo %s in use.\n", stars, passwordstr);
		}
}

int
isrosedigit (c)
char c;
{
int retval;

	retval = isdigit (c);
	if (!retval)
		switch (tolower (c))	{
			case 'l':
			case 'o':
			case 'i':	retval = 1;
					break;
		}
	return retval;
}

static char *
getVia (call)
char *call;
{
char tmp[AXBUF];
static char roseaddr[14];
int i, k;
register struct ax_route *axr;

	roseaddr[0] = 0;
	for(axr = Ax_routes;axr != NULLAXR;axr = axr->next)	{
		pax25(tmp,axr->target);
		if (!strnicmp (call, tmp, strlen (call)))	{
			for (i = 0; i < axr->ndigis; i++)	{
				pax25(tmp,axr->digis[i]);
				if (strlen(tmp) != 6)
					continue;
				for (k = 0; k < 6; k++)	{
					if (!isrosedigit(tmp[k]))
						break;
				}
				if (k != 6)
					continue;
				sprintf (roseaddr, "ROSE:%-6.6s", tmp);
				return (roseaddr);
			}
			break;
		}
	}
	return (roseaddr);
}


/* this is called from the finger-daemon when 'conf' is fingered - WG7J */
int ShowConfUsers(s)
int s;
{
struct convection *p;
int cnt;

	cnt = usprintf (s, headerstr);
	for (p = convections; p; p = p->next)	{
		pwait(NULL);
	        if (p->type == CT_USER)
			cnt += usprintf(s,userdata,p->name,p->host,p->via ? p->via->name : getVia(p->name),
				p->channel,timestring(p->time),p->data ? p->data : empty);
	}
	return (cnt);
}



int CountConfUsers()
{
struct convection *p;
int cnt = 0;

	for (p = convections; p; p = p->next)
	        if (p->type == CT_USER)
			cnt++;
	return (cnt);
}


int CountConfGroups()
{
int cnt = 0;
register struct group *gp;

	for (gp = groups; gp; gp = gp->next)
		cnt++;
	return (cnt);
}

#ifdef UNIXJNOS
#define Quotes "/usr/lib/quotes"

static void
quote_command(cp)
struct convection *cp;
{
char buf2[512];
char searchstr[5];
char *cptr;
FILE *fp;
int dt;
time_t tm;

	if((fp = fopen(Quotes,READ_TEXT)) == NULLFILE)
		return;
	tm = time((time_t *)0);
	dt = atoi (&(ctime(&tm))[8]);
	sprintf (searchstr, "[%-d]", dt);
	while(fgets(buf2,512,fp) != NULLCHAR) 	{
		pwait(NULL);
		if (!strncmp (searchstr, buf2, strlen(searchstr)))
			break;
	}
	if ((cptr = strchr (buf2, '%')) != NULLCHAR)	{
		rip (buf2);
		fclose (fp);
		if ((fp = fopen (++cptr,READ_TEXT)) == NULLFILE)	{
/*			printf ("Couldn't open '%s'\n", &buf2[1]);    */
			return;
		}
	}
	clear_locks ();
	send_msg_to_channel(conversd,cp->channel,quotebanner);
	clear_locks ();
	while(fgets(buf2,512,fp) != NULLCHAR)	{
		if (*buf2 == '[')
			break;
		send_msg_to_channel(conversd,cp->channel,buf2);
		clear_locks ();
	}
	send_msg_to_channel(conversd,cp->channel,trailer);
	fclose (fp);
	return;
}
#else
static void
quote_command(cp)
struct convection *cp;
{
char *p;

	p = getquote();
	if (p)	{
		clear_locks ();
		send_msg_to_channel(conversd,cp->channel,quotebanner);
		clear_locks ();
		send_msg_to_channel(conversd,cp->channel,p);
		clear_locks ();
		send_msg_to_channel(conversd,cp->channel,trailer);
		free (p);
	}
}


#endif


static void
who_command(cp)
struct convection *cp;
{
char buffer[80];
int channel,quick = 0, realname = 0;
struct convection *p;
char whois[16];
#ifdef MAILBOX
int i;
struct mbx *m = 0;
#endif

	convcolorchange (cp, INFOCOLORS);
	whois[0] = buffer[0] = '\0';
	sscanf(cp->ibuf,"%*s %2s %15s",buffer, whois);
	switch(tolower(buffer[0])) {
	case 'q':
		quick = 1;
		break;
 	case 'r':
		realname = 1;
		break;
	}

	if(quick) {
		cp->xmitted += usprintf(cp->fd,"%s  %ss\n", channelstr, userstr);
		clear_locks();
		do {
			channel = -1;
			for(p = convections; p; p = p->next) {
				pwait(NULL);
				if(p->type == CT_USER &&
				  !p->locked &&
				  (channel < 0 || channel == p->channel)) {
					if(channel < 0) {
						channel = p->channel;
						sprintf(buffer,"%7d ",channel);
					}
					strcat(buffer," ");
					if (strlen (buffer) > 65)		{
						cp->xmitted += usprintf(cp->fd,fullstrcr,buffer);
						strcpy (buffer, "         ");
					}
					strcat(buffer,p->nickname);
					p->locked = 1;
				}
			}
			if(channel >= 0) {
				cp->xmitted += usprintf(cp->fd,fullstrcr,buffer);
			}
		} while(channel >= 0);
	} else	if (realname)	{
		cp->xmitted += usprintf (cp->fd, whobanner, userstr, nicknmstr, channelstr);
		for (p = convections; p; p = p->next)		{
			pwait(NULL);
			if (p->type != CT_USER)
				continue;
			if (!*whois || !strnicmp (whois, p->nickname, strlen (whois)) || !strnicmp (whois, p->name, strlen(whois)))
				cp->xmitted += usprintf (cp->fd, "%-16.16s %-10.10s %7d %s\n",
					p->name, p->nickname, p->channel, p->data ? p->data : empty);
			}
	} else {
		cp->xmitted += ShowConfUsers (cp->fd);
	}
#ifdef MAILBOX
	if (!realname)		{
		cp->xmitted += usprintf (cp->fd, "\nMailbox %ss\n", userstr);
#ifdef UNIXJNOS
		m = Mbox;
		for(i = 0; i < NUMMBX; m++,i++)
			if(m != NULLMBX)
#else
		for(i = 0; i < NUMMBX; i++)
			if((m = Mbox[i]) != NULLMBX)
#endif
				if(m->state == MBX_CMD) {
					realname = 1;	/* we found one! */
					if(quick)
				            cp->xmitted += usprintf(cp->fd," BBS %s\n",m->name);
					else
				            cp->xmitted += usprintf(cp->fd,"%-16s BBS@%s\n",m->name,Hostname);
					}
		if (!realname)
			cp->xmitted += usprintf(cp->fd,"(none)\n");
		}
#endif
	cp->xmitted += usprintf(cp->fd,trailer);
}


static void
realname_command(cp)
struct convection *cp;
{
	{
	char name[16];

		name[0] = '\0';
		sscanf (cp->ibuf, string15, name);
		sprintf (cp->ibuf, "/w r %s", name);
	}
	who_command (cp);
}


static void
h_cmsg_command(cp)
struct convection *cp;
{
char *text, *cptr;
int  channel;
char name[NAMELEN+1];

	sscanf (cp->ibuf, "%*s %16s %d ", name, &channel);
	text = &cp->ibuf[0];
	text = skipone (text, 3);
	if (*text == '|')	{
		text++;		/* skip the '|' */
		cptr = strrchr (text, '|');
		if (cptr)
			*cptr = 0;
	} else if (*(text-1) == ' ')	{
		do	{
		        text--;
		} while (*(text-1) == ' ');
		text++;
	}

/*	text[strlen(text) - 1] = 0;	*/
	if (isprint (*text) != 0)
		send_msg_to_channel (name, channel, text);
}


/* Return 1 if the host is to be allowed, or 0 if refused - WG7J */
int Allow_host(int s) {
    struct filter_link *fl;
    struct sockaddr_in fsocket;
    int i = sizeof(struct sockaddr_in);

    if(Filterlinks) {    /* Check for this ip address */
        getpeername(s,(char *)&fsocket,&i);
        for(fl=Filterlinks;fl;fl=fl->next)
            if(fl->addr == fsocket.sin_addr.s_addr)
                return FilterMode;
        /* Not found ! */
        return !FilterMode;
    }
    return 1;
}

static void
h_host_command(cp)
struct convection *cp;
{
char name[NAMELEN+1], features[20];
register struct convection *p;
register struct permlink *pp;

    if(!Allow_host(cp->fd)) {
        bye_command(cp);
        return;
    }
	name[0] = '\0';
	features[0] = '\0';
	cp->features = 0;
	sscanf(cp->ibuf,"%*s %16s %*s %18s",name,features);
    if(name[0] == '\0') {
        bye_command(cp);
        return;
    }
	if (strchr(features,'a')) cp->features |= FEATURE_AWAY;
	if (strchr(features,'d')) cp->features |= FEATURE_FWD;
	if (strchr(features,'m')) cp->features |= FEATURE_MODES;
	if (strchr(features,'p')) cp->features |= FEATURE_LINK;
	if (strchr(features,'u')) cp->features |= FEATURE_UDAT;
	if (strchr(features,'n')) cp->features |= FEATURE_NICK;
/* #define DEBUG */
#ifdef DEBUG
        printf ("Features '%s': %s%s%s%s%s%s\n", name,
        (cp->features & FEATURE_AWAY) ? "a" : "",
        (cp->features & FEATURE_FWD) ? "d" : "",
        (cp->features & FEATURE_MODES) ? "m" : "",
        (cp->features & FEATURE_LINK) ? "p" : "",
        (cp->features & FEATURE_UDAT) ? "u" : "",
        (cp->features & FEATURE_NICK) ? "n" : "");
#endif
 	for(p = convections; p; p = p->next)
		if(!strcmp(p->name,name))	{
			bye_command(p);
			return;
		}
	for(pp = permlinks; pp; pp = pp->next)
		if(!strcmp(pp->name,name) && pp->convection && pp->convection != cp)	{
			bye_command((strcmp(Chostname,name) < 0) ? pp->convection : cp);
			return;
		}
	if(cp->type != CT_UNKNOWN)
		return;
	cp->type = CT_HOST;
    cp->maxq = HMaxQ;
	strcpy(cp->name,name);		/* already allocated */
	update_permlinks(name,cp);
	cp->xmitted += usprintf(cp->fd,hhoststr,Chostname, shortversion, myfeatures);
	for(p = convections; p; p = p->next)
		if(p->type == CT_USER) {
			cp->xmitted += usprintf(cp->fd,huserstr,
				p->name,p->host,p->time,-1,p->channel,p->data);
	                cp->xmitted += usprintf(cp->fd,uaddstr,
                	        p->name,p->host,p->nickname,p->net,p->data ? p->data : "~~");
        }
}


static void
h_invi_command(cp)
struct convection *cp;
{
char fromname[10],toname[10], msg[LINELEN];
int  channel;

	sscanf (cp->ibuf, "%*s %16s %16s %d %s", fromname, toname, &channel, msg);
	send_invite_msg (fromname, toname, channel, msg);
}


static void
h_loop_command(cp)
struct convection *cp;
{
char host[NAMELEN+1];

	sscanf (cp->ibuf, "%*s %s", host);
	log (cp->fd, "conversd rx: LOOP %s", host);
	bye_command (cp);
}


/* Command to take user's personal data across a link - WG7J */
void
h_uadd_command(cp)
struct convection *cp;
{
char *name,*host,*data,*nickname,*channel;
struct convection *p;

    /* do a validity check first */
    if((name = strchr(cp->ibuf,' ')) == NULLCHAR)
        return;
    name++;
    if((host = strchr(name,' ')) == NULLCHAR)
        return;
    *host++ = '\0';
    if((nickname = strchr(host,' ')) == NULLCHAR)
        return;
    *nickname++ = '\0';
    if((channel = strchr(nickname,' ')) == NULLCHAR)
        return;
    *channel++ = '\0';
    if((data = strchr(channel,' ')) == NULLCHAR)
        return;
    *data++ = '\0';
    if ((*data == '~' && data[1] == '~') || *data == '@')
    	*data = 0;
    else
    	data[strlen(data) - 1] = 0;
    /* everything seems fine, now find user ! */
    for(p=convections;p;p=p->next)
        if(!strcmp(p->name,name) && !strcmp(p->host,host)) {
            strcpy (p->nickname, nickname);
            p->net = atoi (channel);
            if(strlen(data))	{
                if(p->data)
                    free(p->data);
                p->data = strdup(data);
                }
            break;
        }
}


static void
initialusers (channel)
int channel;
{
register struct convection *p;

	for(p = convections; p; p = p->next)
		if(p->type == CT_USER && p->channel == channel)
			p->nettime = time(&p->nettime);
}


/* Command to take net's data across a link */
void
h_ndat_command(cp)
struct convection *cp;
{
char *name,*moderator,*password,*cptr;
struct group *p;
int channel, private, logged, nextq, totalq, madenew = 0;

	sscanf (cp->ibuf, "%*s %d %d %d %d %d", &channel, &private, &logged,
		&nextq, &totalq);
	cptr = cp->ibuf;
	cptr = skipone (cptr, 6);
	name = cptr;
	if((moderator = strchr(name,'|')) == NULLCHAR)
		return;
	*moderator++ = '\0';
	if((password = strchr(moderator,'|')) == NULLCHAR)
		return;
	*password++ = '\0';
	if((cptr = strchr(password, '|')) == NULLCHAR)
		return;
	*cptr = '\0';
	/* everything seems fine, now find net ! */
	p = find_group (channel);
	if (p == NULLGROUP)	{
		p = (struct group *)callocw (1, sizeof (struct group));
		p->next = groups;
		groups = p;
		p->channel = channel;
		madenew = 1;
		initialusers (channel);
	}
	p->private = private;
	p->logged = logged;
	p->nextq = nextq;
	p->totalq = totalq;
	strcpy (p->name, name);
	strcpy (p->moderator, moderator);
	strcpy (p->password, password);
	if (private == -1)		{	/* deleting net */
		cp->channel = channel;
		nonetoverride = 1;
		nonet_command (cp);
		madenew = 0;
	}
	if (madenew)	{
		char buf[100];
		sprintf (buf, activenetstr, stars, p->name);
		send_msg_to_channel (conversd, p->channel, buf);
	}		
}


/* Command to take net questions across a link */
void
h_ques_command(cp)
struct convection *cp;
{
char *name = 0,*cptr;
struct group *p;
int channel;
char type;

	sscanf (cp->ibuf, "%*s %d %c %s", &channel, &type, name);
	cptr = cp->ibuf;
	cptr = skipone (cptr, 4);
	p = find_group (channel);
	if (p != NULLGROUP && p->qfile)
		process_question (channel, type, name, cptr);
}


static void
h_umsg_command(cp)
struct convection *cp;
{
char fromname[NAMELEN+1],toname[NAMELEN+1],*text;

	sscanf(cp->ibuf,"%*s %16s %16s",fromname,toname);
	text = &cp->ibuf[0];
	text = skipone (text, 3);
	if(*text)
		send_msg_to_user(fromname,toname,text);
}


static void
h_bump_command(cp)
struct convection *cp;
{
char name[NAMELEN+1];
char channel[10];
register struct convection *p;

	channel[0] = name[0] = 0;
	sscanf(cp->ibuf,"%*s %16s %s",name,channel);
	for(p = convections; p; p = p->next)
		if(p->type == CT_USER && !p->via && (!strcmpi(p->name,name) || !strcmpi(p->nickname, name)))	{
			sprintf (p->ibuf, "/c %s", channel);
			cp->locked = 0;
			channel_command (p);
			break;
			}
}


static void
h_user_command(cp)
struct convection *cp;
{
char host[NAMELEN+1],name[NAMELEN+1], timestr[18];
char *pers;
int  newchannel,oldchannel;
register struct convection *p;
time_t currtime;

	sscanf(cp->ibuf,"%*s %16s %16s %18s %d %d",name,host,timestr,&oldchannel,&newchannel);
	currtime = atol(timestr);
	if (currtime == 0)
		currtime = time((time_t *)0);

	pers = skipone (cp->ibuf, 6);
	rip (pers);

	for(p = convections; p; p = p->next)
        if(p->type == CT_USER) {
            /* new 920705 dl9sau */
            /* If Neighbour2 registers a user on HostX, while someone has already
             * been registered for HostX via Neighbour1, then we definitely have
             * a loop !  We send a loop detect message and then close the link:
             * /..LOOP <Chostname> <myneighbour> <host>
             *
             * The LOOP PREVENTION CODE detects ONLY a loop if it starts at this
             * host. That's, why I suggest this code to be implemented in every
             * conversd implementation.
             */
            if (oldchannel < 0 && p->via != cp && !stricmp(p->host, host)) {
                usprintf(cp->fd,loopstr, Chostname, host,p->via ? p->via->name : Chostname);
                log(cp->fd, "conversd sent: LOOP %s",host);
                bye_command(cp);
                return;
            }
            if(p->channel == oldchannel && p->via == cp && \
                !strcmp(p->name,name) && !strcmp(p->host,host))
			break;
        }
	if(!p) {
		p = (struct convection *)callocw(1,sizeof(struct convection ));
		p->type = CT_USER;
        strcpy(p->name,name);
        strcpy(p->nickname,name); 
        strcpy(p->host,host);
		p->via = cp;
		p->channel = newchannel;
		p->next = convections;
		convections = p;
	}
	p->time = currtime;
	if (p->data)
		free (p->data);
	if ((*pers == '~' && pers[1] == '~') || *pers == '@')
		*pers = 0;
	p->data = strdup(pers);
	if((p->channel = newchannel) < 0)	{
		p->type = CT_CLOSED;
        free_closed_connections();  /*  VE3DTE Apr 5/93 */
    }
	send_user_change_msg(name,host,currtime,oldchannel,newchannel,pers);
}


static struct group *
find_group (channel)
int channel;
{
register struct group *gp;

	for (gp = groups; gp; gp = gp->next)
		if (gp->channel == channel)
			return (gp);
	return (NULLGROUP);
}


static struct group *
get_group (cp)
struct convection *cp;
{
register struct group *gp;

	gp = find_group (cp->channel);
	if (gp == NULLGROUP)	{
		gp = (struct group *)callocw (1, sizeof (struct group));
		gp->next = groups;
		gp->channel = cp->channel;
		strcpy (gp->moderator, cp->name);
		strcpy (gp->password, cp->password);
		groups = gp;
		initialusers (cp->channel);
	}
	return (gp);
}


static struct group *
can_gcontrol (cp)
struct convection *cp;
{
	if (cp->net != cp->channel)	{
		convcolorchange (cp, SYSCOLORS);
		cp->xmitted += usprintf (cp->fd, urnotstr, stars, netcontrolstr);
		return (NOCONTROL);
	}
	return (find_group (cp->channel));
	
}


static void
gname_command(cp, cptr)
struct convection *cp;
char *cptr;
{
register struct group *gp;
	
	convcolorchange (cp, SYSCOLORS);
	cptr = skipone (cptr, 1);
	gp = can_gcontrol (cp);
	if (gp == NOCONTROL)
		return;
	if (*cptr)	{
		char buf[100];
		strncpy (gp->name, cptr, 32);
		clear_locks ();
		sprintf (buf, activenetstr, stars, cptr);
		send_msg_to_channel (conversd, gp->channel, buf);
		update_net_data (gp);
		}
	else
		cp->xmitted += usprintf(cp->fd, activenetstr,stars,gp->name);
}


static void
assignnet_command(cp, cptr)
struct convection *cp;
char *cptr;
{
register struct convection *p;
register struct group *gp;
	
	convcolorchange (cp, SYSCOLORS);
	cptr = skipone (cptr, 1);
	if (cp->net == -1)	{
		cp->xmitted += usprintf (cp->fd, urnotstr, stars, netcontrolstr);
		return;
		}
	if (!*cptr)
		cp->xmitted += usprintf(cp->fd, nouserstr, unknownstr);
	else		{
		gp = find_group (cp->net);
		for(p = convections; p; p = p->next) 	{
			if(p->type == CT_USER && (!strcmpi(p->name,cptr) || !strcmpi(p->nickname, cptr)))
				break;
			}
		if (!p)		{
			cp->xmitted += usprintf(cp->fd, nouserstr, unknownstr);
			return;
			}
		if (p->net != -1)
			cp->xmitted += usprintf(cp->fd, busystr, sosorry, cptr, netcontrolstr);
		else	{
			char buf[80];
			p->net = cp->net;
			cp->net = -1;
			strcpy (gp->moderator, p->name);
			clear_locks ();
			sprintf (buf, urassigned, stars, cp->nickname, netcontrolstr, channelstr, gp->channel);
			send_msg_to_user (conversd, p->name, buf);	
			sprintf (buf, "%s'%s' is now %s\n", stars, p->nickname, netcontrolstr);
			if (p->net != cp->channel)
				cp->xmitted += usprintf(cp->fd, buf);
			send_msg_to_channel (conversd, gp->channel, buf);
			update_user_data (p, 0);
			update_user_data (cp, 0);
			update_net_data (gp);
			}
		}
}



static void
bumpnet_command(cp, cptr)
struct convection *cp;
char *cptr;
{
register struct convection *p, *p2;
register struct group *gp;
char dummy[18];
int thischannel = 0;
	
	convcolorchange (cp, SYSCOLORS);
	dummy[0] = 0;
	sscanf (cptr, string15, dummy);
	cptr = skipone (cptr, 1);
	gp = can_gcontrol (cp);
	if (gp == NOCONTROL)
		return;
	if (!*cptr)
		cp->xmitted += usprintf(cp->fd, nouserstr, unknownstr);
	else		{
		for(p = convections; p; p = p->next) 	{
			if(p->type == CT_USER && (!strcmpi(p->name,dummy) || !strcmpi(p->nickname, dummy)))
				break;
			}
		if (!p)		{
			cp->xmitted += usprintf(cp->fd, nouserstr, unknownstr);
			return;
			}
		if (p->channel != cp->channel)
			cp->xmitted += usprintf(cp->fd, wrongchannel, stars, userstr, dummy, channelstr);
		else	{
			cptr = skipone (cptr, 1);
			thischannel = atoi (cptr);
			if (p->via)	{
			        for (p2 = convections; p2; p2 = p2->next)
			            if(p2->type == CT_HOST)
			                p2->xmitted += usprintf(p2->fd,bumpstr,p->name,thischannel);
				}
			else		{
				sprintf (p->ibuf, "/c %d\n", thischannel);
				cp->locked = 0;
				channel_command (p);
				}
			}
		}
}

static void
process_question(channel, type, name, cptr)
int channel;
char type, *name, *cptr;
{
register struct group *gp;
struct convection *p;
char buf[LINELEN], nmbuf[32], tmbuf[10], nerf[60], *ptr;
int here, sendperm = 1;
time_t currtime;

	gp = find_group (channel);
	if (type == 'a')		{
		if (!gp->totalq && !gp->qfile)		{
			gp->qfile = tmpfile ();
			setbuf (gp->qfile, (char *) 0);
			}
		if (gp->qfile)		{
			gp->totalq++;
			update_net_data (gp);
			sendperm = 0;
			here = ftell (gp->qfile);
			fseek (gp->qfile, 0, SEEK_END);
			currtime = time(&currtime);
			fputs (name, gp->qfile);
			fputc ('\n', gp->qfile);
			fputs (timestring (currtime), gp->qfile);
			fputc ('\n', gp->qfile);
			fputs (cptr, gp->qfile);
			fputc (0, gp->qfile);
			fseek (gp->qfile, here, SEEK_SET);
			sprintf (buf, "%s from '%s'", questionstr, name);
			send_msg_to_user (conversd, gp->moderator, buf);
			}
		}
	else		{
		if (gp->qfile)		{
			sendperm = 0;
			here = ftell (gp->qfile);
			fgets (nmbuf, 16, gp->qfile);
			nmbuf[strlen(nmbuf) - 1] = 0;
			fgets (tmbuf, 9, gp->qfile);
			ptr = buf;
			do	{
				*ptr = fgetc (gp->qfile);
			} while (*ptr++);
			if (type == 'p')	{
				send_msg_to_user (nmbuf, gp->moderator, buf);
				fseek (gp->qfile, here, SEEK_SET);
				}
			else		{
				gp->nextq++;
				clear_locks ();
				if (type != 's')	{
					sprintf (nerf, "%sQuestion #%-d, sent by '%s' at%s", stars, gp->nextq, nmbuf, tmbuf);
					send_msg_to_channel (conversd, gp->channel, nerf);
					clear_locks ();
					send_msg_to_channel (nmbuf, gp->channel, buf);
					}
				else	{
					sprintf (nerf, "%sSkipped Question #%d",stars,gp->nextq);
					send_msg_to_user (conversd, gp->moderator, nerf);
					}
				update_net_data (gp);
				}
			}
		}
	if (sendperm)	{	/* qfile not local, send to remote links */
		for (p = convections; p; p = p->next)
			if(p->type == CT_HOST)
				p->xmitted += usprintf(p->fd,quesstr,
					channel, type, name, cptr);
	}
}

static void
question_command(cp, cptr)
struct convection *cp;
char *cptr;
{
register struct group *gp;
char type;

	cptr = skipone (cptr, 1);
	gp = find_group (cp->channel);
	if (gp == NULLGROUP)	{
		cp->xmitted += usprintf (cp->fd, nonetstr, stars);
		return;
	}
	cp->locked = 0;
	if (cp->net != cp->channel)	{	/* not net control */
		process_question (cp->channel, 'a', cp->nickname, cptr);
		cp->xmitted += usprintf (cp->fd, fullstrcr, questionstr);
		}
	else	{	/* options only available to net control */
		if (*cptr == '?')
			cp->xmitted += usprintf(cp->fd, "%s%d questions (%d unread)\n",stars,gp->totalq, gp->totalq - gp->nextq);
		else		{ /* p=preview nc only, s=skip, other=display */
			if (!gp->totalq || !(gp->totalq - gp->nextq))	{
				cp->xmitted += usprintf (cp->fd, "%sNone\n", stars);
				return;
				}
			type = tolower (*cptr);
			if (type != 'p' && type != 's')
				type = 'r';
			process_question (cp->channel, type, cp->nickname, cptr);
		}
	}
}


static void
log_command(cp, cptr)
struct convection *cp;
char *cptr;
{
register struct group *gp;
int changed = 0;
time_t currtime;

	convcolorchange (cp, SYSCOLORS);
	currtime = time(&currtime);	
	cptr = skipone (cptr, 1);
	gp = can_gcontrol (cp);
	if (gp == NOCONTROL)
		return;
	if (gp->logfile)		{
		fprintf (gp->logfile, closeminutes, stars,timestring (currtime));
		fclose (gp->logfile);
		gp->logfile = (FILE *) 0;
		gp->logged = 0;
		changed = 1;
		cp->xmitted += usprintf(cp->fd, logfilestr, stars, "clos");
	}
	if (*cptr)	{
		gp->logfile = fopen (cptr, APPEND_TEXT);
		if (gp->logfile)	{
			fprintf (gp->logfile, openminutes, stars,timestring (currtime), stars, gp->name, netcontrolstr, gp->moderator);
			cp->xmitted += usprintf(cp->fd, logfilestr, stars, "open");
			gp->logged = 1;
			changed = 0;
			}
		else
			cp->xmitted += usprintf(cp->fd, noopenstr, cptr);
		}
	if (changed)
		update_net_data (gp);
	if (gp->logged)
		glog_command (cp);
}


static void
nonet_command(cp)
struct convection *cp;
{
register struct group *gp, *g;
	
	gp = can_gcontrol (cp);
	if (gp == NOCONTROL)	{
		if (!nonetoverride)
			return;
		else
			gp = find_group (cp->channel);
		}

	if (gp->qfile)
		fclose (gp->qfile);
	if (!nonetoverride)	{
		log_command (cp, empty);
		gp->private = -1;	/* indicator to remotes that we're done */
		update_net_data (gp);
		clear_locks ();
		send_msg_to_channel (conversd, gp->channel, "*** Net complete!\n");
		cp->net = -1;
		update_user_data (cp, 0);
		}
	if (gp == groups)
		groups = gp->next;
	else	for (g = groups; g; g = g->next)
			if (g->next == gp)	{
				g->next = gp->next;
				break;
			}
	free (gp);			
}


static void
gpassword_command(cp, cptr)
struct convection *cp;
char *cptr;
{
char new[16];
register struct group *gp;
	
	convcolorchange (cp, SYSCOLORS);
	new[0] = '\0';
	sscanf(cptr,string15,new);
	gp = can_gcontrol (cp);
	if (gp == NOCONTROL)
		return;
	if (tolower (*cptr) == 'n')	{
		gp->password[0] = '\0';
		cp->xmitted += usprintf(cp->fd, net2str, stars, passwordstr, clearedstr);
		update_net_data (gp);
		return;
	}
	if (*new)	{
		strcpy (gp->password, new);
		cp->xmitted += usprintf(cp->fd, net2str, stars, passwordstr, "set");
		update_net_data (gp);
		}
	else		{
		if (gp->password[0])
			cp->xmitted += usprintf(cp->fd, "%sNet %s: '%s'.\n", stars,
				passwordstr, gp->password);
		else
			cp->xmitted += usprintf(cp->fd, "%sNo Net %s\n", stars,passwordstr);
		}
}


static void
gprivate_command(cp, cptr)
struct convection *cp;
char *cptr;
{
struct group *gp;
	
	convcolorchange (cp, SYSCOLORS);
	gp = can_gcontrol (cp);
	if (gp != NOCONTROL)	{
		gp->private = (tolower (*cptr) == 'c') ? 1 : 0;
		cp->xmitted += usprintf(cp->fd, "%sNet no%s %s\n", stars,
			(gp->private) ? "w" : "t", privatestr);
		update_net_data (gp);
	}
}


static void
group_command(cp, cptr)
struct convection *cp;
char *cptr;
{
struct group *p;

	convcolorchange (cp, SYSCOLORS);
	if (groups == NULLGROUP)
		cp->xmitted += usprintf(cp->fd,"%sNo Active Nets!\n", stars);
	else		{
		cp->xmitted += usprintf(cp->fd,"%s  Net Topic\n", channelstr);
		for(p = groups; p; p = p->next)
			cp->xmitted += usprintf(cp->fd,"%7d  %-32s <%s>\n",p->channel, p->name, p->moderator);
		cp->xmitted += usprintf(cp->fd,trailer);
		}
}


static long seed = 1L;
#define CONV_RAND_MAX 0x7fffffff

static int
conv_rand()
{
	seed = (1103515245L * seed + 12345) & CONV_RAND_MAX;
	return((int) (seed & 077777));
}

static void
conv_randomize()
{
	seed = (time (0) & CONV_RAND_MAX);
}

static int
conv_random(num, base)
int num, base;
{
	return (((long)conv_rand() % num) + base);
}

static char DFAR *suits[] = {"Hearts", "Clubs", "Diamonds", "Spades"};
static char DFAR *cards[] = {"Ace", "Deuce", "Three", "Four", "Five", "Six",
			"Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"};


extern char *SMILEYFile;

static void
smiley_command(cp)
struct convection *cp;
{
int offset, k;
char buff[200];
FILE *fp;

	conv_randomize();
	offset = conv_random (817, 1);
	if (!(fp = fopen (SMILEYFile, "r")))
		return;
	sprintf (buff, "%sSmiley: ", stars);
	for (k = 0; k < offset; k++)	{
		pwait(NULL);
		fgets (&buff[12], 188, fp);
	}
	fclose (fp);
 	clear_locks ();
	send_msg_to_channel(conversd,cp->channel,buff);
}


#ifdef SAMCALLB
extern int SAMoutbytes;

static void
call_command(cp)
struct convection *cp;
{
char buff[16];
int k;

	buff[0] = 0;
	sscanf(cp->ibuf, string15, buff);
	if (*buff)	{
		k = cb_lookup (cp->fd, buff, (FILE *) 0);
		if (SAMoutbytes)	{
			cp->xmitted += SAMoutbytes;
			if (k != 2)
				cp->xmitted += usprintf(cp->fd,trailer);
		} else
			cp->xmitted += usprintf(cp->fd, "%sCallbook server not active at %s\n", stars, Hostname);
	} else
		cp->xmitted += usprintf(cp->fd, "%sNo callsign given\n", stars);
}
#endif


static void
roll_command(cp)
struct convection *cp;
{
int die1, die2;
char buff[80];

	conv_randomize();
	die1 = conv_random (6, 1);
	die2 = conv_random (6, 1);
 	clear_locks ();
	sprintf (buff, rollstr, stars, cp->nickname, die1, die2, die1+die2, trailer);
	send_msg_to_channel(conversd,cp->channel,buff);
}


static void
cut_command(cp)
struct convection *cp;
{
int die1, die2;
char buff[80];

	conv_randomize();
	die1 = conv_random (4, 0);
	die2 = conv_random (13, 0);
 	clear_locks ();
	sprintf (buff, cutstr, stars, cp->nickname, cards[die2], suits[die1], trailer);
	send_msg_to_channel(conversd,cp->channel,buff);
}


static void
glog_command(cp)
struct convection *cp;
{
char buffer[75];
struct convection *p;
struct group *gp;

	gp = can_gcontrol (cp);
	if (gp == NOCONTROL)
		return;
 	clear_locks ();
	send_msg_to_channel (conversd, gp->channel, gloghdr);
 	clear_locks ();
 	sprintf (buffer, glogstr, userstr, nicknmstr, hoststr);
	send_msg_to_channel (conversd, gp->channel, buffer);
	for(p = convections; p; p = p->next)
		if(p->type == CT_USER && gp->channel == p->channel)	{
			sprintf (buffer, "    %-8.8s %-15.15s  %s  %-30.30s\n", p->name,
				p->nickname, timestring (p->nettime), p->host);
			clear_locks ();
			send_msg_to_channel (conversd, gp->channel, buffer);
			}
 	clear_locks ();
	send_msg_to_channel (conversd, gp->channel, trailer);
}


static void
net_command(cp)
struct convection *cp;
{
char *cptr;
register struct group *gp;
	
	convcolorchange (cp, SYSCOLORS);
	cptr = cp->ibuf;
	cptr = skipone (cptr, 1);
	if (!cp->channel && !ConvNet0 && (tolower (*cptr) == 's'))	{
		cp->xmitted += usprintf(cp->fd, "%sNo nets on %s 0\n", stars, channelstr);
		return;
		}
	gp = find_group (cp->channel);
	switch (tolower (*cptr))	{
		case 0:		group_command (cp, cptr);
				break;
		case 'c':	if (tolower (cptr[1]) == 'o')	{
					if (gp == NULLGROUP)
						cp->xmitted += usprintf(cp->fd, nonetstr, stars);
					else
						cp->xmitted += usprintf(cp->fd, "%s is '%s'.\n", netcontrolstr,
							gp->moderator);
					}
				else
					gprivate_command (cp, cptr);
				break;
		case 'r':	if (gp == NULLGROUP)
					cp->xmitted += usprintf(cp->fd, nonetstr, stars);
				else	{
					char new[75];
					cp->xmitted += usprintf(cp->fd, fullstrcr,recheckstr);
					sprintf (new, "%s: %s", recheckstr, cp->nickname);
					send_msg_to_user (conversd, gp->moderator, new);
					}
				break;	
		case 'o':	gprivate_command (cp, cptr);
				break;
		case 'l':	glog_command (cp);
				break;
		case 'i':	if (cp->net != -1)	{
					if (cp->net != cp->channel)
						gp = find_group (cp->net);
					cp->xmitted += usprintf(cp->fd, "%sYou are %s on %s %d\n", stars, netcontrolstr, channelstr,gp->channel);
					gname_command (cp, empty);
					cp->xmitted += usprintf(cp->fd, "%sNet is %s and the minutes are %sbeing recorded\n", stars,
						gp->private ? "closed" : "open", gp->logged ? empty : "not ");
					gpassword_command (cp, empty);
					question_command (cp, "q ?");
					}
				else	glog_command (cp);	/* prints my error msg */
				break; 
		case 't':	gname_command (cp, cptr);
				break;
		case 'm':	log_command (cp, cptr);
				break;
		case 'e':	nonet_command (cp);
				break;
		case 'a':	assignnet_command (cp, cptr);
				break;
		case 'b':	bumpnet_command (cp, cptr);
				break;
		case 'w':	cptr = skipone (cptr, 1);
				if (gp == NULLGROUP)
					cp->xmitted += usprintf(cp->fd, nonetstr, stars);
				else
					send_msg_to_user (cp->nickname, gp->moderator, cptr);
				break;
		case 'q':	question_command (cp, cptr);
				break;
		case '?':
		case 'h':	strcpy (cp->ibuf, "h net");
				help_command (cp);
				break;
		case 'n':
		case 'p':	gpassword_command (cp, cptr);
				break;
		case 's':	if (cp->net != -1)	{
					cp->xmitted += usprintf(cp->fd, "%sAlready a %s\n", stars, netcontrolstr);
					return;
					}
				
				if (gp != NULLGROUP)	{
					cp->xmitted += usprintf(cp->fd, "%sThere's a %s\n", stars, netcontrolstr);
					return;
					}
				gp = get_group (cp);
				cp->net = cp->channel;
				update_user_data (cp, 0);
				gname_command (cp, cptr);
				break;
		default:	cp->xmitted += usprintf(cp->fd,unkcmdstr,unknownstr, cptr, gethelpstr);
		}

}


static struct group *
lookup_group (name)
char *name;
{
register struct group *gp;

	for (gp = groups; gp; gp = gp->next)
		if (!strnicmp(gp->name, name, strlen (name)))
			return (gp);
	return (NULLGROUP);
}



static void
list_groups_command (cp)
struct convection *cp;
{
register struct group *gp;
struct convection *p;
int cnt;

	cp->xmitted += usprintf (cp->fd, grouphdr, stars);
	for (gp = groups; gp; gp = gp->next)	{
		cnt = 0;
		for (p = convections; p; p = p->next)	{
		        if ((p->type == CT_USER) && (p->channel == gp->channel))
				cnt++;
		}
		cp->xmitted += usprintf (cp->fd, "   %5d  %-34.34s  (%02d user%s  %9s  %s\n", gp->channel,
			gp->name, cnt, (cnt > 1) ? "s)" : ") ", gp->private ? "[PRIVATE]" : empty,
			gp->password[0] ? "[PASSWORD]" : empty);
	}
	cp->xmitted += usprintf(cp->fd,trailer);
}



static void
join_command(cp)
struct convection *cp;
{
char s[36];
register struct group *gp;
int k, old;

	s[0] = '\0';
	sscanf(cp->ibuf,"%*s %34s",s);
	if (!s[0])
		return;
	if ((gp = lookup_group(s)) == NULLGROUP)	{
		old = cp->channel;
		for (k = 1; k < MAXCHANNEL; k++)	{
			if ((gp = find_group (k)) == NULLGROUP)
				break;
		}
		cp->channel = k;
		gp = get_group (cp);
		if (cp->net != -1)	{
			int old = cp->channel;
			cp->channel = cp->net;
			nonet_command (cp);
			cp->channel = old;
		}
		cp->net = cp->channel;
		update_user_data (cp, 0);
		gname_command (cp, cp->ibuf);
		cp->channel = old;
	}
	sprintf (cp->ibuf, "/c %d", gp->channel);
	channel_command (cp);
}


static int
gatekeeper (cp, channel)
struct convection *cp;
int channel;
{
register struct group *gp;
char buf[40];

	gp = find_group (channel);
	if (gp == NULLGROUP)
		return (0);		/* okay, there's no net control */
	if (cp->paged == channel)	{	/* we were invited! */
		cp->paged = -1;
		return (0);
	}
	if (cp->net == channel)		/* we control the net */
		return (0);
	pwait(NULL);
	sprintf (buf, "%s'%s' tried to join", stars, cp->name);
	if (gp->private)	{
		cp->xmitted += usprintf (cp->fd, sorry, sosorry, privatestr, channelstr, channel);
		send_msg_to_user (conversd, gp->moderator, buf);
		return (1);
	}
	if (gp->password[0])
		if (strcmpi (cp->password, gp->password))	{
			cp->xmitted += usprintf (cp->fd, sorry, sosorry, passwordstr, channelstr, channel);
			send_msg_to_user (conversd, gp->moderator, buf);
			return (1);
		}
	return (0);			
}

void whois_command(cp)
struct convection *cp;
{
struct convection *p;
char name[36];
int found = 0;

	name[0] = '\0';
	sscanf(cp->ibuf,"%*s %34s",name);
	if (!name[0])
		cp->xmitted += usprintf(cp->fd,"%sNeed username...", stars);
	else {
		for (p = convections; p; p = p->next)	{
			pwait(NULL);
			if ((p->type == CT_USER) &&(!stricmp(p->name, name) || !stricmp(p->nickname, name))) {
				found = 1;
				cp->xmitted += usprintf (cp->fd, headerstr);
				cp->xmitted += usprintf(cp->fd,userdata,
					p->name,p->host,p->via ? p->via->name : getVia(p->name),
					p->channel,timestring(p->time),p->data ? p->data : empty);
				if (strcmp(p->name, p->nickname))
					cp->xmitted += usprintf(cp->fd, "\n      Nickname: %s", p->nickname);
				if (*p->data && strcmp(p->data, "@"))
					cp->xmitted += usprintf(cp->fd, "\n      Personal: %s", p->data);
				cp->xmitted += usprintf(cp->fd, "\n       Channel: %d", p->channel);
				break;
			}
		} /* for convections */
		if (!found)
			cp->xmitted += usprintf(cp->fd,"%sUser not found...", stars);
	}
	cp->xmitted += usprintf(cp->fd, "\n%s", trailer);
}


static void
conv_usflush (s)
int s;
{
register struct usock *up;

	if((up = itop(s)) == NULLUSOCK)
		return;

	if(up->obuf == NULLBUF)
		return;
	if (len_p (up->obuf) < 2400)
		return;
	usflush (s);
}


struct cmdtable {
	char  *name;
	void (*fnc) __ARGS((struct convection *));
	int  states;
	} DFAR cmdtable[] = {
		"?",		cmdsummary_command,	CM_USER,
		"accept",	accept_command,		CM_USER,
		"bye",		bye_command,		CM_USER,
		"channel",	channel_command,	CM_USER,
#ifdef SAMCALLB
		"call",		call_command,		CM_USER,
#endif
		"color",	color_command,		CM_USER,
#ifdef LZW
		"compressed",   compressed_command,	CM_USER,
#endif
		"cut",		cut_command,		CM_USER,
		"exit",		bye_command,		CM_USER,
		"groups",	list_groups_command,	CM_USER,
		"help",		help_command,		CM_USER,
		"invite",	invite_command,		CM_USER,
		"join",		join_command,		CM_USER,
		"links",	links_command,		CM_USER,
		"msg",		msg_command,		CM_USER,
		"name",		name_command,		CM_UNKNOWN,
		"net",		net_command,		CM_USER,
		"news",		news_command,		CM_USER,
		"nickname",	nickname_command,	CM_USER,
		"nonickname",   nickname_command,	CM_USER,
		"nopassword",	password_command,	CM_USER,
		"page",		invite_command,		CM_USER,
		"password",	password_command,	CM_USER,
		"personal",	personal_command,	CM_USER,
		"quit",		bye_command,		CM_USER,
		"quote",	quote_command,		CM_USER,
		"realname",     realname_command,	CM_USER,
		"roll",		roll_command,		CM_USER,
		"send",		msg_command,		CM_USER,
		"smiley",	smiley_command,		CM_USER,
		"sounds",	sounds_command,		CM_USER,
		"time",		time_command,		CM_USER,
		"who",		who_command,		CM_USER,
		"whois",	whois_command,		CM_USER,
		"write",	msg_command,		CM_USER,

		"\377\200bump", h_bump_command,		CM_HOST,
		"\377\200cmsg", h_cmsg_command,		CM_HOST,
		"\377\200host", h_host_command,		CM_UNKNOWN,
		"\377\200invi", h_invi_command,		CM_HOST,
		"\377\200loop", h_loop_command,		CM_HOST,
		"\377\200ndat", h_ndat_command,		CM_HOST,
		"\377\200ques", h_ques_command,		CM_HOST,
		"\377\200uadd", h_uadd_command,		CM_HOST,
		"\377\200umsg", h_umsg_command,		CM_HOST,
		"\377\200user", h_user_command,		CM_HOST,
		0,		0,			0,
    };


static void
process_commands(cp,m)
struct convection *cp;
struct mbx *m;
{
char arg[40];
int arglen,size;
int firstcmd = 1;
char *ccp;
struct cmdtable *cmdp;

	Conflogins++;
	if (m)
		firstcmd = 0;
	if (m == (struct mbx *) 1)
		m = NULLMBX;
	if (confMOTD != NULL && !cp->features)
		cp->xmitted += usprintf(cp->fd, "\n%s\n", confMOTD);
	for (;;) 	{
loop:
		if (m)	{
			usflush (cp->fd);    /* might not be neccesary */
#ifdef MBXTDISC
			/* Restart the inactivity timer - WG7J */
			start_timer(&m->tdisc);
#endif
		}
		conv_usflush(cp->fd);
		pwait(NULL);
		if (cp->type == CT_CLOSED)
			break;
		memset (cp->ibuf, 0, LINELEN);
		if((size = recvline (cp->fd, cp->ibuf, LINELEN-1)) <= 0)
			break;
		cp->received += size;
		clear_locks ();
		cp->locked = 1;
		if (*cp->ibuf == '/') 	{
			ccp = &cp->ibuf[1];
			arg[0] = '\0';
			sscanf (ccp, fullstr, arg);
			arglen = strlen (arg);
			for (cmdp = cmdtable; cmdp->name; cmdp++)	{
				if (!strncmpi (cmdp->name, arg, (unsigned int)arglen))	{
					if (cmdp->states & (1 << cp->type))		{
						(*cmdp->fnc)(cp);
						if (cp->type != CT_UNKNOWN)
							firstcmd = 0;
			    			}
					if (firstcmd)
						cp->xmitted += usprintf(cp->fd,namecmdstr, stars);
	                		goto loop;
					}
				}
			if (firstcmd)
				cp->xmitted += usprintf (cp->fd, namecmdstr, stars);
			else if (cp->type == CT_USER)
				cp->xmitted += usprintf (cp->fd, unkcmdstr, unknownstr, arg, gethelpstr);
			goto loop;
			}
		if (firstcmd)	{
			/* only before a valid user */
			if (!cp->features)	/* not a pending host link */
				cp->xmitted += usprintf(cp->fd,namecmdstr, stars);
			goto loop;
			}
#ifndef TNOS_68K
	        if ((ccp = strpbrk (cp->ibuf, "\r\n")) != NULLCHAR)
#else
	        if ((ccp = strpbrk (cp->ibuf, "\r\l")) != NULLCHAR)
#endif
			*ccp = '\0';
#ifdef NOCTLCHARS
		if (isprint (cp->ibuf[0]) != 0 && cp->type == CT_USER)
#else
		if (cp->type == CT_USER)
#endif
			send_msg_to_channel (cp->nickname, cp->channel, cp->ibuf);
		}
	sockblock(cp->fd,SOCK_BLOCK);
	if (m)
		setflush (cp->fd, -1);
	bye_command (cp);
	free_closed_connections ();
}


/* Incoming convers session */
void
conv_incom(s,t,p)
int s;
void *t;
void *p;
{
struct convection *cp;

	sockowner(s,Curproc);	/* We own it now */
/*	sockmode(s,SOCK_BINARY);	*/
	sockmode(s,SOCK_ASCII);
	sockblock(s,SOCK_NOTXBLOCK);   /* prevent backlogs ! */
	cp = alloc_connection(s);
	cp->channel = CChannel;
#ifndef UNIXJNOS
	statlog ("CONFERENCE - New Telnet User");
#endif
	
	{
	struct permlink *pl;

	for(pl = permlinks; pl; pl = pl->next)	{
		if(pl->fd == s) {
			pl->convection = cp;
			cp->xmitted += usprintf(s,hhoststr,Chostname, shortversion, myfeatures);
			cp->features = 1;	/* to suppress error msgs */
			break;
			}
	}

	if(pl == NULLPERMLINK) {
		cp->xmitted += usprintf(cp->fd,
			"%s%s %s",conferencestr, Chostname, gethelpstr);
		if (ConvHeader)
			usprintf(cp->fd,namecmdstr, stars);
		}
	}
    process_commands(cp,(struct mbx *) 0);
}


#ifdef MAILBOX
/* this is for Mailbox users */
void
mbox_converse(fd, name, channel, mbox)
int fd;
char *name;
int channel;
struct mbx *mbox;
{
struct convection *cp;

	sockblock(fd,SOCK_NOTXBLOCK);   /* prevent backlogs ! */
	cp = alloc_connection(fd);
	cp->channel = (channel == -1) ? CChannel : channel;
	strcpy(cp->name,name);
	strcpy (cp->nickname, cp->name);
	strcpy(cp->host,Chostname);
	cp->type = CT_USER;
        cp->flags &= ~CLOSE_SOCK;     /* do not close socket on exit */
	setflush(fd,'\n');       /* automatic flushing each line */
	cp->xmitted += usprintf(fd, "%s%s %s", conferencestr, Chostname, gethelpstr);
	announce_new_user (cp);
	set_personal(cp);
	clear_locks();
	cp->locked = 1; /* send to everyone but ourself */
	mystatus (cp, (int) -1);
	process_commands(cp,(mbox) ? mbox : (struct mbx *) 1);
}
#endif /* MAILBOX */

void conversWriteall (str)
char *str;
{
struct convection *p;

	for (p = convections; p; p = p->next)
	        if (p->type == CT_USER)
			usprintf(p->fd,"%s SYSOP: %s\n",amessage, str);
}


static void
convcolorchange (p, str)
struct convection *p;
unsigned char *str;
{
#ifndef UNIX
int old;

    	if (p->flags & USE_COLOR)	{
    		old = Curproc->output;
    		Curproc->output = p->fd;
	    	p->xmitted += colorchange (str, p->colorset);
	    	Curproc->output = old;
	}
#endif
}


static void
cmdsummary_command(cp)
struct convection *cp;
{
int i;
char buf[82];
struct cmdtable *cmdp = cmdtable;
int width = 11, count = 7;

	cp->xmitted += usprintf(cp->fd,summary, stars);
	memset(buf,' ',sizeof(buf));
	buf[77] = '\n';
	buf[78] = '\0';
	for(i=0;cmdp->name;cmdp++,i = ((i+1) % count)){
		if ((unsigned char)*cmdp->name == 255)
			break;
		strncpy(&buf[i * width],cmdp->name,strlen(cmdp->name));
		if(i == (count - 1)){
			cp->xmitted = usprintf(cp->fd, "%s ", buf);
			memset(buf,' ',77);
		}
	}
	if (i)
		cp->xmitted += usprintf(cp->fd, buf);
	cp->xmitted += usprintf(cp->fd, trailer);	
}

#endif /* CONVERS */
