/* 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
 */
  
#include <time.h>
#include <ctype.h>
#ifdef  UNIX
#include <sys/types.h>
#include <sys/stat.h>
#endif
#ifdef MSDOS
#include <io.h>
#endif
#include "global.h"
#ifdef CONVERS
#include "mailbox.h"
#include "netuser.h"
#include "pktdrvr.h"
#include "timer.h"
#include "cmdparse.h"
#include "usock.h"
#include "socket.h"
#include "session.h"
#include "files.h"
#include "mailutil.h"
  
#define LINK    1
#define space
  
char Chostname[CNAMELEN+1];
void conv_incom __ARGS((int s,void *t,void *p));
int ConvUsers;
int ConvHosts;
  
#ifdef space
static char cnumber[] = "*** Channel numbers must be in the range 0..%d.\n";
#else
static char cnumber[] = "* range 0..%d.\n";
#endif
  
#define MAXCHANNEL  32767
#define LINELEN     256
/* The convers daemon stack needs to be AT LEAST 4 times the LINELEN !
 * since quiet a few of the commands have local vars of size 2 or 3 * LINELEN.
 */
#define CDAEMONSTACK 2048
#define CLINKSTACK 1024
#define MAX_WAITTIME    (60*60*3)
#define NAMELEN 10
  
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 */
    struct convection *via;     /* Pointer to neighbor host */
    char *data;                 /* room for some personal data */
    int channel;            /* Channel number */
    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
    /* 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 */
    unsigned long received;          /* Number of bytes received */
    unsigned long xmitted;           /* Number of bytes transmitted */
    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)
  
struct filter_link {
    struct filter_link *next;
    int32 addr;
};
#define NULLFL ((struct filter_link *) 0)
  
#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
  
static void free_connection __ARGS((register struct convection *cp));
static char *formatline __ARGS((char  *prefix,char *text));
static char  *timestring __ARGS((long  gmt));
static void clear_locks __ARGS((void));
static void send_msg_to_user __ARGS((char *fromname,char *toname,char *text));
static void send_user_change_msg __ARGS((char *name,char *host,int oldchannel,int newchannel));
static void send_invite_msg __ARGS((char *fromname,char *toname,int channel));
static void personal_command __ARGS((struct convection *cp));
static void bye_command __ARGS((struct convection *cp));
static void send_msg_to_channel __ARGS((char *fromname,int channel,char *text));
static void free_closed_connections __ARGS((void));
static struct convection *alloc_connection __ARGS((int fd));
static void process_commands __ARGS((struct convection *cp,struct mbx *m));
static void set_personal __ARGS((struct convection *cp));
static void version_command __ARGS((struct convection *cp));
  
static struct filter_link *Filterlinks;
static int FilterMode;
static struct permlink *permlinks;
extern char Ccall[AXALEN];
  
static int docfilter __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 doclink __ARGS((int argc,char *argv[],void *p));
static int doct4 __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 dotdisc __ARGS((int argc,char *argv[],void *p));
static int docdefaultchannel __ARGS((int argc,char *argv[],void *p));
  
static struct cmds DFAR Ccmds[] = {
    "channel",  docdefaultchannel, 0, 0, NULLCHAR,
    "filter",   docfilter,  0, 0, NULLCHAR,
    "hmaxq",    dohmaxq,    0, 0, NULLCHAR,
    "hostname", dochostname,0, 0, NULLCHAR,
    "interface",dociface,   0, 0, NULLCHAR,
    "maxwait",  docmaxwait, 0, 0, NULLCHAR,
#ifdef AX25
    "mycall",   doconfcall, 0, 0, NULLCHAR,
#endif
#ifdef LINK
    "link",     doclink,    0, 0, NULLCHAR,
#endif
#ifdef AX25
    "t4",       doct4,      0, 0, NULLCHAR,
#endif
    "tdisc",    dotdisc,    0, 0, NULLCHAR,
    "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("%s\n",pax25(tmp,Ccall));
        return 0;
    }
    if(setcall(Ccall,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,"Conf. redundancy timer (sec)",argc,argv);
}
#endif /* AX25 */
  
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,"Re-link max wait (sec)",argc,argv);
}
  
int HMaxQ = 5*1024;
  
/* Set max qlimit for host links */
static int
dohmaxq(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setint(&HMaxQ,"Max. Host Queue (bytes)",argc,argv);
}
  
int UMaxQ = 1024;
  
/* Set max qlimit for user links */
static int
doumaxq(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setint(&UMaxQ,"Max. User Queue (bytes)",argc,argv);
}
  
int CDefaultChannel;
  
/* Set the initial channel */
static int
docdefaultchannel(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setint(&CDefaultChannel,"Default channel",argc,argv);
}
  
int32 Ctdiscinit;
  
/* Set convers redundancy timer */
static int
dotdisc(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setlong(&Ctdiscinit,"redundancy timer (sec)",argc,argv);
}
  
static int
dochostname(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    if(argc == 1)
        tprintf("%s\n",Chostname);
    else {
        strncpy(Chostname,argv[1],CNAMELEN);
        Chostname[CNAMELEN] = '\0';
    }
    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("Mode is: %s\n", FilterMode ? "Accept" : "Refuse");
            for(fl=Filterlinks;fl;fl=fl->next)
                tprintf("%s\n",inet_ntoa(fl->addr));
        }
        return 0;
    }
    if(!stricmp(argv[1],"mode")) {
        if(argc == 2)
            tprintf("Mode is: %s\n", FilterMode ? "Accept" : "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("%10s: %s\n",pl->name,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 > 2) {
        strncpy(pl->name,argv[2],NAMELEN);
        update_permlinks(pl->name,NULLCONNECTION);
    } else
        strcpy(pl->name,"Unknown");
    if(!Linker)
        Linker = newproc("Clinker",CLINKSTACK,connect_permlinks,0,0,NULL,0);
  
    return 0;
}
#endif
  
static int
dociface(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setflag(argc,argv[1],IS_CONV_IFACE,argv[2]);
}
  
/* Stop convers server */
int
conv0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    int16 port;
  
    if(argc < 2)
        port = IPPORT_CONVERS;
    else
        port = atoi(argv[1]);
#ifdef LINK
    if(Linker)
        killproc(Linker);
#endif
    return stop_tcp(port);
}
  
/* Start up convers server */
int
conv1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    int16 port;
  
    if(argc < 2)
        port = IPPORT_CONVERS;
    else
        port = atoi(argv[1]);
  
    return start_tcp(port,"CONVERS Server",conv_incom,CDAEMONSTACK);
}
  
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;
    /* if(cp->data)   free() checks for NULL; smaller code :-) */
    free(cp->data);
    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;
    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)) {
                /* notify everyone of this special :-) occasion */
            bye_command(p);
                /* 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(;;) {
        pause(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",CDAEMONSTACK,conv_incom,s,(void *)TELNET,NULL,0) == NULLPROC){
                shutdown(s,2);  /* blow it out of the water :-) */
                close_s(s);
            }
        }
        /* This is now called from the garbage collect process, such that it
         * checks even when the linker process isn't running! - WG7J
        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(struct convection *p) {
    if(p->flags & USE_SOUND)
        p->xmitted += usputs(p->fd,"");
}
  
static void
send_user_change_msg(name,host,oldchannel,newchannel)
char  *name,*host;
int  oldchannel,newchannel;
{
    register struct convection *p;
  
    for(p = convections; p; p = p->next) {
        if(p->type == CT_USER && !p->via && !p->locked) {
            if(p->channel == oldchannel) {
                if(newchannel >= 0) {
                    p->xmitted += usprintf(p->fd,
                    "*** %s switched to channel %d.\n",
                    name,newchannel);
                } else
                    p->xmitted += usprintf(p->fd,
                    "*** %s signed off.\n",name);
                p->locked = 1;
            }
            if(p->channel == newchannel) {
                send_sounds(p);
                p->xmitted += usprintf(p->fd,
                "*** %s signed on.\n",name);
                p->locked = 1;
            }
        }
        if(p->type == CT_HOST && !p->locked) {
            p->xmitted += usprintf(p->fd,
            "/\377\200USER %s %s %d %d %d\n",
            name,host,0,oldchannel,newchannel);
            p->locked = 1;
        }
    }
    return;
}
  
#ifdef Oldcode
  
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;
  
    for(;;) {
        while(isspace(uchar(*f)))
            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;
        }
        do {
            *t++ = ' ';
            l++;
        } while(l < PREFIXLEN);
        while(lw--) {
            *t++ = *f++;
            l++;
        }
    }
}
  
static void
send_msg_to_user(fromname,toname,text)
char  *fromname,*toname,*text;
{
    register struct convection *p;
    char buffer[2*LINELEN];
  
    for(p = convections; p; p = p->next) {
        if(p->type == CT_USER && !strcmp(p->name,toname))
            if(p->via) {
                if(!p->via->locked) {
                    p->via->xmitted += usprintf(p->via->fd,
                    "/\377\200UMSG %s %s %s\n",fromname,toname,text);
                    p->via->locked = 1;
                }
            } else {
                if(!p->locked) {
                    if(strcmp(fromname,"conversd")) {
                        sprintf(buffer,"<*%s*>:",fromname);
                        p->xmitted += usputs(p->fd,formatline(buffer,text));
                    } else {
                        p->xmitted += usputs(p->fd,text);
                        p->xmitted += usputc('\n');
                    }
                    p->locked = 1;
                }
            }
    }
    return;
}
  
static void
send_msg_to_channel(fromname,channel,text)
char  *fromname;
int  channel;
char  *text;
{
    char  buffer[3*LINELEN];
    register struct convection *p;
  
    for(p = convections; p; p = p->next) {
        if(p->type == CT_USER && p->channel == channel)
            if(p->via) {
                if(!p->via->locked) {
                    p->via->xmitted += usprintf(p->via->fd,
                    "/\377\200CMSG %s %d %s\n",fromname,channel,text);
                    p->via->locked = 1;
                }
            } else {
                if(!p->locked) {
                    sprintf(buffer,"<%s>:",fromname);
                    p->xmitted += usputs(p->fd,formatline(&buffer[0],text));
                    p->locked = 1;
                }
            }
    }
}
  
#else /* Oldcode */
  
/* Returns a formatted version of the given text.
 * The prefix appears at the beginning of the text, and each
 * line after the first will be indented to column PREFIXLEN.
 * All whitespace (SPACE and TAB) will be replaced by a single
 * SPACE character,
 * Lines will be filled to be CONVLINELEN characters long, wrapping
 * words as necessary.
 *
 * This uses an static internal  buffer rather than one passed by
 * the caller.  This increases the static memory used by the program
 * even if converse isn't active.  If we passed a buffer  allocated
 * on the stack, the process's stack would have to be large enough.
 */
static char *
formatline(prefix, text)
char  *prefix, *text;
{
  
#   define PREFIXLEN    10
#   define CONVLINELEN  79
#   define FMTBUFLEN    LINELEN
  
    static char          buf[FMTBUFLEN];
    register char       *f, *t;
    register int         l, lw;
    register int         left = FMTBUFLEN-2;
    /* Runs of characters in delims[] will be collapsed into a single
       space by formatline().
     */
    char                *delims = " \t\n\r";
/*
#   define BPUTC(c)     if (left > 0) { left--; *t++ = (c); } else
*/
#   define BPUTC(c)     do{ if (left > 0) { left--; *t++ = (c); }} while(0)
  
    /* Copy prefix into buf; set l to length of prefix.
     */
    l = 0;
    for (f = prefix, t = buf; *f; ) {
        BPUTC (*f++);
        l++;
    }
  
    f = text;
  
    for (;;) {
        /* Skip leading spaces */
        while (isspace(uchar(*f)))
            f++;
  
        /* Return if nothing more or no room left */
        if (!*f || (left <= 0)) {
            *t++ = '\n';        /* don't use BPUTC; do even if !left */
            *t   = '\0';
            return buf;
        }
  
        /* Find length of next word (seq. of non-blanks) */
        lw = strcspn (f, delims);
  
        /* If the word would extend past end of line, do newline */
        if (l > PREFIXLEN && (l + 1 + lw) > CONVLINELEN) {
            BPUTC ('\n');
            l = 0;
        }
  
        /* Put out a single space */
        do {
            BPUTC (' ');
            l++;
        } while(l < PREFIXLEN);
  
        /* Put out the word */
        while(lw--) {
            BPUTC (*f++);
            l++;
        }
    }
#   undef BPUTC
}
  
  
static void
send_msg_to_user(fromname,toname,text)
char  *fromname,*toname,*text;
{
    register struct convection *p;
  
    for (p = convections; p; p = p->next) {
        if(p->type == CT_USER && !strcmp(p->name,toname))
            if(p->via) {
                if(!p->via->locked) {
                    p->via->xmitted += usprintf(p->via->fd,
                    "/\377\200UMSG %.10s %.10s %.220s\n",
                    fromname, toname, text);
                    p->via->locked = 1;
                }
            } else {
                if(!p->locked) {
                    if(strcmp(fromname,"conversd")) {
                        char prefix[NAMELEN+10];
                        char *buf;
  
                        sprintf(prefix,"<*%.10s*>:",fromname);
  
                        buf = formatline (prefix,text);
                        p->xmitted += strlen (buf);
                        (void) usputs (p->fd, buf);
                    } else {    /* not from conversd */
                        p->xmitted += strlen (text);
                        (void) usputs (p->fd, text);
                    }   /* not from conversd */
                    p->locked = 1;
                } /* if not locked */
            }   /* not via */
    } /* for */
}
  
static void
send_msg_to_channel(fromname,channel,text)
char  *fromname;
int  channel;
char  *text;
{
    register struct convection *p;
  
    for (p = convections; p; p = p->next) {
        if(p->type == CT_USER && p->channel == channel)
            if(p->via) {
                if(!p->via->locked) {
                    p->via->xmitted += usprintf(p->via->fd,
                    "/\377\200CMSG %.10s %d %.220s\n",
                    fromname, channel, text);
                    p->via->locked = 1;
                }
            } else {
                if(!p->locked) {
                    char         prefix[NAMELEN+10];
                    char        *buf;
  
                    sprintf(prefix,"<%.10s>:", fromname);
  
                    buf = formatline (prefix, text);
                    p->xmitted += strlen (buf);
                    (void) usputs (p->fd, buf);
  
                    p->locked = 1;
                } /* not locked */
            } /* not via */
    } /* for */
}
  
  
#endif /* Oldcode */
  
  
extern char *Months[];      /* in smtpserv.c */
  
static char  *
timestring(gmt)
long  gmt;
{
    static char  buffer[10];
    struct tm *tm;
    time_t currtime;
  
    time(&currtime);
    tm = localtime(&gmt);
    if(gmt + 24 * 60 * 60 > currtime)
        sprintf(buffer," %2d:%02d",tm->tm_hour,tm->tm_min);
    else
        sprintf(buffer,"%-3.3s %2d",Months[tm->tm_mon],tm->tm_mday);
    return buffer;
}
  
#ifdef space
char invitetext[] = "\n*** Message from %s at%s ...\nPlease join convers channel %d.\n\n";
char mbinvitetext[] = "\n*** Message from %s at%s ...\nPlease join convers by typing 'CONV %d' !\n\n";
char responsetext[] = "*** Invitation sent to %s @ %s";
#else
char invitetext[] = "\n*** Msg frm %s at%s ...\nPse join ch. %d.\n\n";
char mbinvitetext[] = "\n*** Msg frm %s at%s ...\nPse hit 'CONV %d' to join convers.\n\n";
char responsetext[] = "*** sent to %s @ %s";
#endif
char cnvd[] = "conversd";
  
static void
send_invite_msg(fromname,toname,channel)
char  *fromname,*toname;
int  channel;
{
  
    char buffer[LINELEN];
    struct convection *p;
#ifdef MAILBOX
    struct mbx *m;
#endif
    time_t currtime;
  
    currtime = time(&currtime);
  
#ifdef MAILBOX
    /* Check users in the mailbox that aren't active */
    for(m=Mbox;m;m=m->next){
        if(m->state == MBX_CMD && !stricmp(m->name,toname)) {
            usprintf(m->user,mbinvitetext,fromname,timestring(currtime),channel);
            usflush(m->user);
            clear_locks();
            sprintf(buffer,responsetext,toname,"BBS@");
            strcat(buffer,Hostname);
            send_msg_to_user(cnvd,fromname,buffer);
            return;
        }
    }
#endif
  
    /* check the current convers users */
    for(p = convections; p; p = p->next) {
        if(p->type == CT_USER && !stricmp(p->name,toname)) {
            if(p->channel == channel) {
                clear_locks();
                sprintf(buffer,"*** User %s is already on this channel.\n",toname);
                send_msg_to_user(cnvd,fromname,buffer);
                return;
            }
            if(!p->via && !p->locked) {
                p->xmitted += usprintf(p->fd,invitetext,fromname, \
                timestring(currtime),channel);
                clear_locks();
                sprintf(buffer,responsetext,toname,Chostname);
                send_msg_to_user(cnvd,fromname,buffer);
                return;
            }
            if(p->via && !p->via->locked) {
                p->via->xmitted += usprintf(p->via->fd,
                "/\377\200INVI %s %s %d\n",fromname,toname,channel);
                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,
            "/\377\200INVI %s %s %d\n",fromname,toname,channel);
        }
    }
    return;
}
  
static void
bye_command(cp)
struct convection *cp;
{
    struct convection *p;
  
    switch(cp->type) {
        case CT_UNKNOWN:
            cp->type = CT_CLOSED;
            break;
        case CT_USER:
            cp->type = CT_CLOSED;
            clear_locks();
            send_user_change_msg(cp->name,cp->host,cp->channel,-1);
            ConvUsers--;
            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->channel,-1);
                }
            ConvHosts--;
            break;
        case CT_CLOSED:
            break;
    }
}
  
static void
channel_command(cp)
struct convection *cp;
{
    char  s[7];
    int  newchannel;
  
    s[0] = '\0';
    sscanf(cp->ibuf,"%*s %6s",s);
    if(s[0] == '\0') {
#ifdef space
        cp->xmitted += usprintf(cp->fd,"*** You are on channel %d.\n",cp->channel);
#else
        cp->xmitted += usprintf(cp->fd,"* On channel %d.\n",cp->channel);
#endif
        return;
    }
    newchannel = atoi(s);
    if(newchannel < 0) {
        /*  || newchannel > MAXCHANNEL) { */
        cp->xmitted += usprintf(cp->fd,cnumber,MAXCHANNEL);
        return;
    }
    if(newchannel == cp->channel) {
        cp->xmitted += usprintf(cp->fd,
        "*** Already on channel %d.\n",cp->channel);
        return;
    }
    send_user_change_msg(cp->name,cp->host,cp->channel,newchannel);
    cp->channel = newchannel;
    cp->xmitted += usprintf(cp->fd,"*** Now on channel %d.\n",cp->channel);
    return;
}
  
static void
help_command(cp)
struct convection *cp;
{
#ifdef space
    cp->xmitted += usputs(cp->fd,"Commands may be abbreviated. Commands are:\n");
    cp->xmitted += usputs(cp->fd,"/? or /HELP     Print help information\n");
    cp->xmitted += usputs(cp->fd,"/BYE            Terminate the convers session\n");
    cp->xmitted += usputs(cp->fd,"/CHANNEL        Switch to channel n\n");
    cp->xmitted += usputs(cp->fd,"/EXIT           Terminate the convers session\n");
    cp->xmitted += usputs(cp->fd,"/INV user       Invite user to join your channel\n");
    cp->xmitted += usputs(cp->fd,"/LINKS [L]      List all connections to other hosts\n");
    cp->xmitted += usputs(cp->fd,"/MSG user text  Send a private message to user\n");
    cp->xmitted += usputs(cp->fd,"/PERSONAL text  Set or show some personal text\n");
    cp->xmitted += usputs(cp->fd,"/QUIT           Terminate the convers session\n");
    cp->xmitted += usputs(cp->fd,"/SOUNDS y|n     Turn bell on or off\n");
    cp->xmitted += usputs(cp->fd,"/WHO [QUICK]    List all users and their channel numbers\n");
    cp->xmitted += usputs(cp->fd,"/WR user text   Send a private message to user\n"
    "***\n");
#else
    cp->xmitted += usputs(cp->fd,"no help\n");
#endif
    return;
}
  
static void
version_command(cp)
struct convection *cp;
{
    usprintf(cp->fd,"%s Convers Server.\n",shortversion);
}
  
static void
invite_command(cp)
struct convection *cp;
{
    char toname[NAMELEN+1];
    char *cp1;
  
    toname[0] = '\0';
    sscanf(cp->ibuf,"%*s %10s",toname);
    if((cp1=strchr(toname,'@')) != NULLCHAR)
        *cp1 = '\0';
    if(toname[0] != '\0')
        send_invite_msg(cp->name,toname,cp->channel);
    return;
}
  
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)
        if(pc->type == CT_HOST)
            num += usprintf(s,
            (full) ?
            "%-10s Connected    %s               %7lu %7lu\n" :
            "%-10s Connected    %s\n",
            pc->name,
            timestring(pc->time),
            pc->received,
            pc->xmitted);
  
    for(pp = permlinks; pp; pp = pp->next)
        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 += usputs(s,"***\n");
    return num;
}
  
static void
links_command(cp)
struct convection *cp;
{
    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;
}
  
static void
msg_command(cp)
struct convection *cp;
{
  
    char dummy[LINELEN],toname[NAMELEN+1],*text;
    register struct convection *p;
  
    toname[0] = '\0';
    sscanf(cp->ibuf,"%s %10s",dummy,toname);
    text = &cp->ibuf[0];
    text += strlen(dummy) + strlen(toname) + 2;
  
    if(!*text)
        return;
    for(p = convections; p; p = p->next)
        if(p->type == CT_USER && !strcmp(p->name,toname))
            break;
    if(!p)
        cp->xmitted += usprintf(cp->fd,"*** No such user: %s.\n",toname);
    else
        send_msg_to_user(cp->name,toname,text);
    return;
}
  
/* Set some personal data, like name and qth - WG7J */
#define PERSONAL_LEN 31
  
static void
personal_command(cp)
struct convection *cp;
{
    struct convection *p;
    char *cp2;
  
    if((cp2 = strchr(cp->ibuf,' ')) != NULLCHAR) {
        cp2++;
        if(*cp2) {  /* there actually is an argument */
            if(cp->data)
                free(cp->data);
            rip(cp->ibuf);      /* get rid of ending '\n' */
            if(strlen(cp2) > PERSONAL_LEN)
                *(cp2+PERSONAL_LEN) = '\0';
            cp->data = strdup(cp2);
            /* update all links too ! - WG7J */
            for(p=convections;p;p=p->next)
                if(p->type == CT_HOST)
                    p->xmitted += usprintf(p->fd,"/\377\200UDAT %s %s %s\n",
                    cp->name,cp->host,cp->data);
            return;
        }
    }
    cp->xmitted += usprintf(cp->fd,"*** data set to: %s\n", \
    cp->data ? cp->data : "" );
    return;
}
  
/* find the personal information for this user */
void set_personal(struct convection *cp) {
    FILE *fp;
    char *cp1;
  
    if((fp = fopen(Cinfo,"r")) == NULL)
        return;
    while(fgets(cp->ibuf,LINELEN,fp) != NULL) {
        cp1 = cp->ibuf;
        /* find end of name */
        while(*cp1 != ' ' && *cp1 != '\t' && *cp1 != '\0')
            cp1++;
        if(!*cp1)
            continue;
        *cp1 = '\0';
        if(stricmp(cp->name,cp->ibuf))
            continue;
        /* Found personal data ! */
        *cp1 = ' ';
        fclose(fp);
        personal_command(cp);
        return;
    }
    fclose(fp);
}
  
void SendMotd(int s) {
    FILE *fp;
  
    if((fp=fopen(ConvMotd,"r")) != NULL) {
        sendfile(fp, s, ASCII_TYPE, 0, NULL);
        fclose(fp);
    }
}
  
/* protected by ftpusers file - WG7J */
static void
name_command(cp)
struct convection *cp;
{
    int newchannel = CDefaultChannel;
    char dummy[7];
    char *path;
    int pwdignore;
    long privs;
  
    cp->name[0] = '\0';
    dummy[0] = '\0';
    sscanf(cp->ibuf,"%*s %10s %6s",cp->name,dummy);
    if(dummy[0] != '\0')
        newchannel = atoi(dummy);
    if(cp->name[0] == '\0')
        return;
    /* now check with ftpusers file - WG7J */
    if((path = mallocw(MBXLINE)) == NULLCHAR)
        return;
    pwdignore = 1;
    privs = userlogin(cp->name,NULLCHAR,&path,MBXLINE,&pwdignore,"confperm");
    free(path);
    if(privs & NO_CONVERS)
        return;
    strlwr(cp->name);
    strcpy(cp->host,Chostname);
    cp->type = CT_USER;
    cp->xmitted += usprintf(cp->fd,
    "Conference @ %s  Type /HELP for help.\n",Chostname);
    /* Send the motd */
    SendMotd(cp->fd);
    if(dummy[0] != '\0' && newchannel < 0) {
        /* || newchannel > MAXCHANNEL) { */
        cp->xmitted += usprintf(cp->fd,cnumber,MAXCHANNEL);
    } else
        cp->channel = newchannel;
    send_user_change_msg(cp->name,cp->host,-1,cp->channel);
    set_personal(cp);
    ConvUsers++;
    return;
}
  
/* 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) {   /* There is an argument */
            if(*cp2 == 'n' || *cp2 == 'N') {   /* Turn it off */
                cp->flags &= ~USE_SOUND;
            } else
                cp->flags |= USE_SOUND;
            return;
        }
    }
    if(cp->flags & USE_SOUND)
        usputs(cp->fd,"*** Sounds on\n");
    else
        usputs(cp->fd,"*** Sounds off\n");
    return;
}
  
/* Print a user display, return the number of characters sent */
int ShowConfUsers(int s,int quick,char *name) {
    int num,channel;
    struct convection *p;
#ifdef MAILBOX
    struct mbx *m;
#endif
    char buffer[LINELEN];
  
    if(quick) {
        num = usputs(s,"Channel Users\n");
        clear_locks();
        do {
            channel = -1;
            for(p = convections; p; p = p->next) {
                if(p->type == CT_USER &&
                    !p->locked &&
                (channel < 0 || channel == p->channel)) {
                    if(channel < 0) {
                        channel = p->channel;
                        sprintf(buffer,"%7d",channel);
                    }
                    strcat(buffer," ");
                    strcat(buffer,p->name);
                    p->locked = 1;
                }
            }
            if(channel >= 0) {
                num += usputs(s,buffer);
                num += usputc(s,'\n');
            }
        } while(channel >= 0);
    } else {
        num = usputs(s,"User       Host       Via        Channel   Time Personal\n");
        for(p = convections; p; p = p->next) {
            if(p->type == CT_USER) {
                if(name == NULL || *name == '\0' || !stricmp(p->name,name))
                    num += usprintf(s,"%-10s %-10s %-10s %7d %s %s\n",
                    p->name,
                    p->host,
                    p->via ? p->via->name : "",
                    p->channel,
                    timestring(p->time),
                    p->data ? p->data : "" );
            }
        }
    }
#ifdef MAILBOX
    for(m=Mbox;m;m=m->next) {
        if(m->state == MBX_CMD) {
            if(quick)
                num += usprintf(s," BBS %s\n",m->name);
            else
                num += usprintf(s,"%-10s BBS@%s\n",m->name,Hostname);
        }
    }
#endif
    num += usputs(s,"***\n");
    return num;
}
  
static void
who_command(cp)
struct convection *cp;
{
    char buffer[LINELEN];
    int quick = 0;
  
    buffer[0] = '\0';
    sscanf(cp->ibuf,"%*s %s",buffer);
    if(buffer[0] == 'q')
        quick = 1;
    cp->xmitted += ShowConfUsers(cp->fd,quick,buffer);
    return;
}
  
static void
h_cmsg_command(cp)
struct convection *cp;
{
    char *text;
    int  channel;
    char name[LINELEN],dummy[40];
  
    sscanf(cp->ibuf,"%s %10s %d",dummy,name,&channel);
    text = &cp->ibuf[0];
    text += strlen(dummy) + strlen(name) + 2;
    while(isspace(*text) == 0)
        text++;
    text++;
    if(isprint(*text) != 0)
        send_msg_to_channel(name,channel,text);
    return;
}
  
/* 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];
    struct convection *p;
    struct permlink *pp;
  
    if(!Allow_host(cp->fd)) {
        bye_command(cp);
        return;
    }
    name[0] = '\0';
    sscanf(cp->ibuf,"%*s %10s",name);
    if(name[0] == '\0') {
        bye_command(cp);
        return;
    }
    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,"/\377\200HOST %s\n",Chostname);
    for(p = convections; p; p = p->next)
        if(p->type == CT_USER) {
            cp->xmitted += usprintf(cp->fd,
            "/\377\200USER %s %s %d %d %d\n",
            p->name,p->host,0,-1,p->channel);
            if(p->data)
                cp->xmitted += usprintf(cp->fd,
                "/\377\200UDAT %s %s %s\n",
                p->name,p->host,p->data);
        }
    ConvHosts++;
    return;
}
  
static void
h_invi_command(cp)
struct convection *cp;
{
    char fromname[NAMELEN+1],toname[NAMELEN+1];
    int  channel;
  
    sscanf(cp->ibuf,"%*s %10s %10s %d",fromname,toname,&channel);
    send_invite_msg(fromname,toname,channel);
    return;
}
  
static void
h_loop_command(cp)
struct convection *cp;
{
    char host[NAMELEN+1];
  
    sscanf(cp->ibuf,"%*s %10s",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_udat_command(cp)
struct convection *cp;
{
    char *name,*host,*data;
    int len;
    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((data = strchr(host,' ')) == NULLCHAR)
        return;
    *data++ = '\0';
    rip(data);      /* rip the '\n' */
    if((len=strlen(data)) == 0)
        return;
    if(len > PERSONAL_LEN)
        *(data+PERSONAL_LEN) = '\0';
  
    /* everything seems fine, now find user ! */
    for(p=convections;p;p=p->next) {
        if(!strcmp(p->name,name) && !strcmp(p->host,host)) {
            if(p->data)
                free(p->data);
            p->data = strdup(data);
        }
        /* update over other links  Apr 12/93 VE3DTE */
        if(p->type == CT_HOST && !p->locked)
            p->xmitted += usprintf(p->fd,"/\377\200UDAT %s %s %s\n",
            name,host,data);
    }
}
  
static void
h_umsg_command(cp)
struct convection *cp;
{
    char dummy[NAMELEN+1],fromname[NAMELEN+1],toname[NAMELEN+1],*text;
  
    sscanf(cp->ibuf,"%s %10s %10s",dummy,fromname,toname);
    text = &cp->ibuf[0];
    text += strlen(dummy) + strlen(fromname) + strlen(toname) + 3;
    if(*text)
        send_msg_to_user(fromname,toname,text);
    return;
}
  
static void
h_user_command(cp)
struct convection *cp;
{
    char host[3*NAMELEN+1],name[3*NAMELEN+1];
    int  newchannel,oldchannel;
    struct convection *p;
    time_t currtime;
  
    currtime = time(&currtime);
  
    sscanf(cp->ibuf,"%*s %s %s %*s %d %d",name,host,&oldchannel,&newchannel);
    /* Make sure the fields are not longer */
    host[NAMELEN] = name[NAMELEN] = '\0';
  
    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,"/\377\200LOOP %s %s %s\n", \
                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->host,host);
        p->via = cp;
        p->channel = oldchannel;
        p->time = currtime;
        p->next = convections;
        convections = p;
    }
    if((p->channel = newchannel) < 0) {
        p->type = CT_CLOSED;
        free_closed_connections();  /*  VE3DTE Apr 5/93 */
    }
    send_user_change_msg(name,host,oldchannel,newchannel);
    return;
}
  
struct cmdtable {
    char  *name;
    void (*fnc)(struct convection *);
    int  states;
};
struct cmdtable DFAR cmdtable[] = {
    "?",        help_command,       CM_USER,
    "bye",      bye_command,        CM_USER,
    "channel",  channel_command,    CM_USER,
    "exit",     bye_command,        CM_USER,
    "help",     help_command,       CM_USER,
    "invite",   invite_command,     CM_USER,
    "links",    links_command,      CM_USER,
    "msg",      msg_command,        CM_USER,
    "name",     name_command,       CM_UNKNOWN,
    "personal", personal_command,   CM_USER,
    "quit",     bye_command,        CM_USER,
    "sounds",   sounds_command,     CM_USER,
    "version",  version_command,    CM_USER,
    "who",      who_command,        CM_USER,
    "write",    msg_command,        CM_USER,
  
    "\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\200udat", h_udat_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[LINELEN];
    int arglen,size;
    char *ccp;
    struct cmdtable *cmdp;
  
    for(;;) {
        loop:
        if(cp->type == CT_CLOSED)
            break;
        setflush(cp->fd,'\n');
        usflush(cp->fd);
        memset(cp->ibuf,0,LINELEN);
        if(cp->type != CT_HOST)
            alarm(Ctdiscinit * 1000L);
        if((size = recvline(cp->fd,cp->ibuf,LINELEN-1)) <= 0)
            break;
        alarm(0L);
        cp->received += size;
        clear_locks();
        cp->locked = 1;
        if(*cp->ibuf == '/') {
            ccp = &cp->ibuf[1];
            arg[0] = '\0';
            sscanf(ccp,"%s",arg);
            arglen = strlen(arg);
#ifdef notdef
            /* We are about to parse a command; most likely there
             * is alot of output; try to avoid fragmenting that
             * data by doing our own flushing ! - WG7J
             */
            setflush(cp->fd,-1);
/* n5knx: VE3DTE, N2RJT et alia, find data losses occur if we stop flushing
          after NL.  This must surely be a bug, but where? */
#endif
            for(cmdp = cmdtable; cmdp->name; cmdp++) {
                if(!strncmpi(cmdp->name,arg,arglen)) {
                    if(cmdp->states & (1 << cp->type))
                        (*cmdp->fnc)(cp);
                    goto loop;
                }
            }
            if(cp->type == CT_USER)
                cp->xmitted += usprintf(cp->fd,
                "*** Unknown command '/%s'. Type /HELP for help.\n",arg);
            goto loop;
        }
        if((ccp = strpbrk(cp->ibuf,"\r\n")) != NULLCHAR)
            *ccp = '\0';
        if(isprint(cp->ibuf[0]) != 0 && cp->type == CT_USER)
            send_msg_to_channel(cp->name,cp->channel,cp->ibuf);
    }
    bye_command(cp);
    sockblock(cp->fd,SOCK_BLOCK);
    free_closed_connections();
}
  
/* Incoming convers session */
void
conv_incom(s,t,p)
int s;
void *t;
void *p;
{
    struct convection *cp;
    struct permlink *pl;
  
    sockowner(s,Curproc);   /* We own it now */
    sockmode(s,SOCK_BINARY);
    sockblock(s,SOCK_NOTXBLOCK);   /* prevent backlogs ! */
    cp = alloc_connection(s);
    cp->channel = CDefaultChannel;
  
    for(pl = permlinks; pl; pl = pl->next)
        if(pl->fd == s) {
            pl->convection = cp;
            cp->xmitted += usprintf(s,"/\377\200HOST %s\n",Chostname);
        }
  
    if(pl == NULLPERMLINK) {
#ifdef AX25
        if((int)t == AX25TNC) { /* figure out call from socket */
            struct usock *up;
            char *chrp;
  
            if((up = itop(s)) == NULLUSOCK) {
                free_connection(cp);
                return;
            }
            sockmode(s,SOCK_ASCII);
            pax25(cp->name,up->cb.ax25->remote);
            if((chrp=strchr(cp->name,'-')) != NULL)
                *chrp = '\0';
            strlwr(cp->name);
            strcpy(cp->host,Chostname);
            cp->type = CT_USER;
            cp->xmitted += usprintf(s,
            "Conference @ %s  Type /HELP for help.\n",Chostname);
            SendMotd(s);
            clear_locks();
            cp->locked = 1; /* send to everyone but ourself */
            send_user_change_msg(cp->name,cp->host,-1,CDefaultChannel);
            set_personal(cp);
            ConvUsers++;
        } else
#endif
            usputs(cp->fd,"\npse login with '/n <call>'\n\n");
    }
    process_commands(cp,NULLMBX);
}
  
#ifdef MAILBOX
  
/* this is for Mailbox users */
void
mbox_converse(struct mbx *m,int channel)
{
    struct convection *cp;
    int oldflush;
  
    sockblock(m->user,SOCK_NOTXBLOCK);  /* prevent backlogs ! */
    oldflush = setflush(m->user,'\n');  /* automatic line flushing */
    cp = alloc_connection(m->user);
    cp->channel = channel;
    strcpy(cp->name,m->name);
    strcpy(cp->host,Chostname);
    cp->type = CT_USER;
    cp->flags &= ~CLOSE_SOCK;     /* do not close socket on exit */
    cp->xmitted += usprintf(m->user,
    "Conference @ %s  Type /HELP for help.\n",Chostname);
    SendMotd(m->user);
    clear_locks();
    cp->locked = 1; /* send to everyone but ourself */
    send_user_change_msg(cp->name,cp->host,-1,cp->channel);
    set_personal(cp);
    ConvUsers++;
  
    process_commands(cp,m);
  
    setflush(m->user,oldflush);
  
}
#endif /* MAILBOX */
  
#endif /* CONVERS */
