/*
  Pirate Bulletin Board System
  Copyright (C) 1990, Edward Luke
  
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 1, or (at your option)
  any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  
  
  Email address:
  lush@Athena.EE.MsState.EDU
  */
#include "bbs.h"
#include "io.h"

#include <sys/types.h>
#ifdef lint
#include <sys/uio.h>
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>

#ifdef CTRL
#undef CTRL
#define CTRL(X) (X&037)
#endif


char *
  ModeType(mode)
{
    switch(mode)
      {
        case IDLE:
          return "" ;
        case ULDL:
          return "UL/DL" ;
        case TALK:
          return "Talk Mode" ;
        case NEW:
          return "New User" ;
        case CHAT:
          return "Chat Mode" ;
        case POSTING:
          return "Posting" ;
        case MAIL:
          return "Mail Mode" ;
        case READNEW:
          return "Reading" ;
        case IRC:
          return "IRC Chat" ;
        default:
          return "Udef Mode" ;
      }
}

printcuent(uentp)
struct user_info *uentp ;
{
    struct user utmp ;
    static int i ;
    
    if(uentp == NULL) {
        move(3,0) ;
        prints("%-12s %-20s %-20s %-9s\n","User Id","User Name","From","Mode") ;
        i = 3 ;
        return 0;
    }
    if(!uentp->active || !uentp->pid)
      return 0;
    if(currentuser.userlevel < 255 && uentp->invisible)
      return 0;
    if(kill(uentp->pid,0) == -1)
      return 0;
    if(i == 22) {
        int ch ;
        standout() ;
        prints("--MORE--") ;
        standend() ;
        while((ch = igetch()) != EOF) {
            if(ch == '\n' || ch == '\r' || ch == ' ')
              break ;
            bell() ;
        }
        move(3,0) ;
        prints("%-12s %-20s %-20s %-9s\n","User Id","User Name","From","Mode") ;
        i = 3 ;
        clrtobot() ;
    }
    get_record(PASSFILE,&utmp,sizeof(utmp),uentp->uid) ;
    prints("%-12s %-20s %-20s %-9s %s\n",utmp.userid,utmp.username,uentp->from,
           ModeType(uentp->mode),uentp->invisible?"(CLOAK)":"");
    i++ ;
    return 0 ;
}

listcuent(uentp)
struct user_info *uentp ;
{
    struct user utmp ;
    
    if(uentp == NULL) {
        CreateNameList() ;
        return 0;
    }
    if(uentp->uid == usernum)
      return 0;
    if(!uentp->active || !uentp->pid)
      return 0;
    if(uentp->mode == ULDL)
      return 0;
    if(currentuser.userlevel < 255 && uentp->invisible)
      return 0;
    if(kill(uentp->pid,0) == -1)
      return 0;
    get_record(PASSFILE,&utmp,sizeof(utmp),uentp->uid) ;
    AddNameList(utmp.userid) ;
    return 0 ;
}

dumb_list(uentp)
struct user_info *uentp ;
{
    struct user utmp ;
    
    if(uentp == NULL) {
        printf("%-16s %-25s %9s %s\n","User Id","User Name","Mode","From") ;
        return 0 ;
    }
    if(!uentp->active || !uentp->pid)
      return 0 ;
    if(kill(uentp->pid,0) == -1 && errno == ESRCH)
      return 0 ;
    get_record(PASSFILE,&utmp,sizeof(utmp),uentp->uid) ;
    printf("%-16s %-25s %9s %s\n",utmp.userid,utmp.username,
           ModeType(uentp->mode),uentp->from);
    return 1 ;
}

creat_list()
{
    listcuent(NULL) ;
    apply_record(ULIST,listcuent,sizeof(struct user_info)) ;
}

t_users()
{
    report("talk user list") ;
    printcuent(NULL) ;
    if(apply_record(ULIST,printcuent,sizeof(struct user_info)) == -1) {
        prints("No Users Exist\n") ;
        pressreturn() ;
        return 0;
    }
    clrtobot() ;
    return 0;
}

dumb_user_list()
{
    dumb_list(NULL) ;
    if(apply_record(ULIST,dumb_list,sizeof(struct user_info)) == -1) {
        printf("No Users Exist") ;
    }
}

do_all_list(uentp)
struct user_info *uentp ;
{
    struct user utmp ;
    char *ctime() ;
    
    if(uentp == NULL) {
        printf("%-14s %-25s %-10s\n","User Id","User Name","Termtype") ;
        return 0 ;
    }
    /*      if(kill(uentp->pid,0) == -1)
            return 0 ;*/
    get_record(PASSFILE,&utmp,sizeof(utmp),uentp->uid) ;
    if(utmp.username[0] == '\0')
      return 0 ;
    printf("%-14s %s",utmp.userid,ctime(&(utmp.lastlogin)));
    printf("%-14s %-25s %-10s %-9s %-16s %-6s\n",utmp.userid,utmp.username
           ,utmp.termtype,ModeType(uentp->mode),uentp->from,
           (uentp->active)?"ACTIVE":"") ;
    return 1 ;
}


list_all_users()
{
    do_all_list(NULL) ;
    if(apply_record(ULIST,do_all_list, sizeof(struct user_info)) == -1) {
        printf("No Users Exist") ;
    }
}

count_active(uentp)
struct user_info *uentp ;
{
    struct user utmp ;
    static int count ;
    if(uentp == NULL) {
        int c = count ;
        count = 0 ;
        return c ;
    }
    if(!uentp->active || !uentp->pid)
      return 0 ;
    if(kill(uentp->pid,0) == -1)
      return 0 ;
    count++ ;
    return 1 ;
}

int num_active_users()
{
    count_active(NULL) ;
    apply_record(ULIST,count_active,sizeof(struct user_info)) ;
    return count_active(NULL) ;
}

#include <sys/time.h>

t_cmpuids(uid,up)
int uid ;
struct user_info *up ;
{
    return (uid == up->uid) ;
}

t_talk()
{
    char uident[STRLEN] ;
    int tuid ;
    struct user_info uin ;
    
    move(2,0) ;
    prints("<Enter Userid>\n") ;
    move(1,0) ;
    clrtoeol() ;
    prints("To: ") ;
    creat_list() ;
    namecomplete(NULL,uident) ;
    if(uident[0] == '\0') {
        clear() ;
        return 0 ;
    }
    if(!(tuid = getuser(uident)) || tuid == usernum) {
        move(2,0) ;
        prints("Bad User ID\n") ;
        pressreturn() ;
        move(2,0) ;
        clrtoeol() ;
        return -1 ;
    }
    search_record(ULIST,&uin,sizeof(uin),t_cmpuids,tuid) ;
    if(uin.mode == ULDL) {
        move(2,0) ;
        prints("User is Uploading or Downloading\n") ;
        pressreturn() ;
        move(2,0) ;
        clrtoeol() ;
        return -1 ;
    }
    if(!uin.active || (kill(uin.pid,0) == -1)) {
        move(2,0) ;
        prints("User not logged in\n") ;
        pressreturn() ;
        move(2,0) ;
        clrtoeol() ;
        return -1 ;
    } else {
        int sock, msgsock, length ;
        struct sockaddr_in server ;
        extern struct user_info uinfo ;
        char c ;
        char buf[512] ;
        
        sprintf(buf,"Talk to '%s'",uident) ;
        report(buf) ;
        sock = socket(AF_INET, SOCK_STREAM, 0) ;
        if(sock < 0) {
            perror("opening stream socket\n") ;
            return -1 ;
        }
        
        server.sin_family = AF_INET ;
        server.sin_addr.s_addr = INADDR_ANY ;
        server.sin_port = 0 ;
        if(bind(sock, (struct sockaddr *) & server, sizeof server ) < 0) {
            perror("binding stream socket") ;
            return -1 ;
        }
        length = sizeof server ;
        if(getsockname(sock, (struct sockaddr *) &server, &length) < 0) {
            perror("getting socket name") ;
            return -1 ;
        }
        uinfo.sockactive = YEA ;
        uinfo.sockaddr = server.sin_port ;
        uinfo.destuid = tuid ;
        substitute_record(ULIST,&uinfo, sizeof(uinfo), utmpent) ;
        kill(uin.pid,SIGUSR1) ;
        clear() ;
        prints("Polling user.....\nTYPE CTRL-D to terminate\n") ;
        listen(sock,1) ;
        add_io(sock,20) ;
        while(YEA) {
            int ch ;
            ch = igetch() ;
            if(ch == I_TIMEOUT) {
                move(0,0) ;
                prints("Ringing Party Again.\n") ;
                bell() ;
                if(kill(uin.pid,SIGUSR1) == -1) {
                    move(0,0) ;
                    prints("Party has logged off\n\n\n") ;
                    pressreturn() ;
                    return -1 ;
                }
                continue ;
            }
            if(ch == I_OTHERDATA)
              break ;
            if(ch == '\004') {
                add_io(0,0) ;
                close(sock) ;
                uinfo.sockactive = NA ;
                uinfo.destuid = 0 ;
                substitute_record(ULIST,&uinfo, sizeof(uinfo), utmpent) ;
                clear() ;
                return 0 ;
            }
        }
        
        msgsock = accept(sock, (struct sockaddr *)0, (int *) 0) ;
        if(msgsock == -1) {
            perror("accept") ;
            return -1 ;
        }
        add_io(0,0) ;
        close(sock) ;
        uinfo.sockactive = NA ;
        uinfo.destuid = 0 ;
        substitute_record(ULIST,&uinfo, sizeof(uinfo), utmpent) ;
        read(msgsock,&c,sizeof c) ;
        if(c == 'y')
          do_talk(msgsock) ;
        else {
            clear() ;
            prints("Talk request not accepted.\n") ;
            pressreturn() ;
        }
        close(msgsock) ;
        clear() ;
    }
    return 0 ;
}

extern int talkrequest ;
struct user_info  ui ;

int cmpunums(unum,up)
int unum ;
struct user_info *up ;
{
    if(!up->active)
      return 0 ;
    return (unum == up->destuid) ;
}

int
  searchuserlist(unum)
{
    return search_record(ULIST,&ui,sizeof(ui),cmpunums,unum) ;
}

talkreply()
{
    struct user au ;
    int tuid, a ;
    struct hostent *h ;
    char buf[512] ;
    char hostname[STRLEN] ;
    struct sockaddr_in sin ;
    
    talkrequest = NA ;
    tuid = searchuserlist(usernum) ;
    if(tuid == 0)
      return 0;
    if(!ui.sockactive)
      return 0;
    get_record(PASSFILE,&au,sizeof(au),ui.uid) ;
    clear() ;
    if(ui.sockaddr == 0) {
        prints("YOU ARE BEING KILLED BY USER '%s' %s\n",au.userid,
               au.username) ;
        pressreturn() ;
        kill(ui.pid,SIGHUP) ;
        exit(-1) ;
    }
    prints("Would you like to talk with '%s' %s?\n",au.userid, au.username) ;
    getdata(1,0,"(Yes or No): ",buf,STRLEN,DOECHO,NULL) ;
    gethostname(hostname,STRLEN) ;
    if(!(h = gethostbyname(hostname))) {
        perror("gethostbyname") ;
        return -1 ;
    }
    bzero(&sin, sizeof sin) ;
    sin.sin_family = h->h_addrtype ;
    bcopy(h->h_addr, &sin.sin_addr,h->h_length) ;
    sin.sin_port = ui.sockaddr ;
    a = socket(sin.sin_family,SOCK_STREAM,0) ;
    if((connect(a,&sin, sizeof sin))) {
        perror("connect failed") ;
        return -1 ;
    }
    if(buf[0] == '\0')
      buf[0] = 'y' ;
    if(buf[0] == 'Y')
      buf[0] = 'y' ;
    write(a,buf,1) ;
    if(buf[0] != 'y') {
        close(a) ;
        clear() ;
        return 0 ;
    }
    do_talk(a) ;
    close(a) ;
    clear() ;
    return 0 ;
}

do_talk_char(sline,eline,curln,curcol,wordbuf,wordbuflen,ch)
int sline, eline ;
int *curln, *curcol ;
char *wordbuf ;
int *wordbuflen ;
int ch ;
{
    extern int dumb_term ;
    if(isprint(ch)) {
        if(*curcol != 79) {
            wordbuf[(*wordbuflen)++] = ch ;
            if(ch == ' ')
              *wordbuflen = 0 ;
            move(*curln,(*curcol)++) ;
            prints("%c",ch) ;
            return ;
        }
        if(ch == ' ' || *wordbuflen >=78) {
            (*curln)++ ;
            *curcol = 0 ;
            if(*curln > eline)
              *curln = sline ;
            if((*curln) != eline) {
                move((*curln)+1,0) ;
                clrtoeol() ;
            }
            move(*curln,*curcol) ;
            clrtoeol() ;
            *wordbuflen = 0 ;
            return ;
        }
        move(*curln,(*curcol) - *wordbuflen) ;
        clrtoeol() ;
        (*curln)++ ;
        *curcol = 0 ;
        if(*curln > eline)
          *curln = sline ;
        if((*curln) != eline) {
            move((*curln)+1,0) ;
            clrtoeol() ;
        }
        move(*curln,*curcol) ;
        clrtoeol() ;
        wordbuf[*wordbuflen] = '\0' ;
        if(dumb_term)
          prints("\n") ;
        prints("%s%c",wordbuf,ch) ;
        *curcol = (*wordbuflen)+1 ;
        *wordbuflen = 0 ;
        return ;
    }
    switch(ch) {
      case 0x08: /* ^H */
      case '\177':
        if(dumb_term)
          ochar(0x08) ; /* ^H */
        if(*curcol == 0) {
            if(sline == 0)
              bell() ;
            return;
        }
        (*curcol)-- ;
        move(*curln,*curcol) ;
        if(!dumb_term)
          prints(" ") ;
        move(*curln,*curcol) ;
        if(*wordbuflen)
          (*wordbuflen)-- ;
        return ;
      case 0x0d: /* ^M */
      case 0x0a: /* ^J */
        if(dumb_term)
          prints("\n") ;
        (*curln)++ ;
        *curcol = 0 ;
        if(*curln > eline)
          *curln = sline ;
        if((*curln) != eline) {
            move((*curln)+1,0) ;
            clrtoeol() ;
        }
        move(*curln,*curcol) ;
        clrtoeol() ;
        *wordbuflen = 0 ;
        return ;
      case 0x07: /* ^G */
        bell() ;
        return ;
      default:
        break ;
    }
    return ;
}

char talkobuf[80] ;
int talkobuflen ;
int talkflushfd ;

talkflush()
{
    if(talkobuflen) 
      write(talkflushfd,talkobuf,talkobuflen) ;
    talkobuflen = 0 ;
}



do_talk(fd)
int fd ;
{
    int i ;
    int myln,mycol,myfirstln,mylastln  ;
    int itsln,itscol,itsfirstln,itslastln ;
    char itswordbuf[80],mywordbuf[80] ;
    int itswordbuflen,mywordbuflen ;
    extern int t_lines ;
    extern struct user_info uinfo ;
    
    itswordbuflen = 0 ;
    mywordbuflen = 0 ;
    talkobuflen = 0 ;
    talkflushfd = fd ;
    uinfo.mode = TALK ;
    substitute_record(ULIST,&uinfo, sizeof(uinfo), utmpent) ;
    clear() ;
    myfirstln = 0 ;
    mylastln = (t_lines-1)/2 - 1 ;
    move(mylastln+1,0) ;
    prints("-------------------------------------------------------------------------------") ;
    itsfirstln = mylastln+2 ;
    itslastln = (t_lines -1) ;
    myln = myfirstln ;
    mycol = 0 ;
    itsln = itsfirstln ;
    itscol = 0 ;
    move(myln,mycol) ;
    add_io(fd,0) ;
    add_flush(talkflush) ;
    while(YEA) {
        int ch ;
        ch = igetch() ;
        if(ch == I_OTHERDATA) {
            char data[80] ;
            int datac ;
            register int i ;
            
            datac = read(fd,data,80) ;
            if(datac<=0) 
              break ;
            for(i=0;i<datac;i++)
              do_talk_char(itsfirstln,itslastln,&itsln,&itscol,itswordbuf,
                           &itswordbuflen,data[i]) ;
        } else {
            if(ch == 0x04 || ch == 0x03) /* ^D or ^C */
              break ;
            if(isprint(ch) || ch == 0x08 || ch == '\177' || ch == 0x07
               || ch == 0x0d || ch == 0x0a) { /* ^H, G, M or J */
                talkobuf[talkobuflen++] = ch ;
                if(talkobuflen == 80)
                  talkflush() ;
                do_talk_char(myfirstln,mylastln,&myln,&mycol,mywordbuf,
                             &mywordbuflen,ch) ;
            } else bell() ;
        }
    }
    add_io(0,0) ;
    talkflush() ;
    add_flush(NULL) ;
    uinfo.mode = IDLE ;
    substitute_record(ULIST,&uinfo, sizeof(uinfo), utmpent) ;
}


t_irc()
{
    char buf[512] ;
    extern struct user_info uinfo ;
    
    uinfo.mode = IRC ;
    substitute_record(ULIST, &uinfo, sizeof(uinfo), utmpent) ;
    sprintf(buf,"irc %s mars.ee.msstate.edu",currentuser.userid) ;
    reset_tty() ;
    do_exec(buf,NULL) ;
    restore_tty() ;
    uinfo.mode = IDLE ;
    substitute_record(ULIST, &uinfo, sizeof(uinfo), utmpent) ;
    clear() ;
}
