/* NOS User Session control
 * Copyright 1991 Phil Karn, KA9Q
 *
 * Mods by PA0GRI
 */
#include "global.h"
#include "config.h"
#include "mbuf.h"
#include "proc.h"
#include "ftpcli.h"
#include "icmp.h"
#include "telnet.h"
#include "tty.h"
#include "session.h"
#include "hardware.h"
#include "socket.h"
#include "cmdparse.h"
#include "rlogin.h"
#include "commands.h"
#include "main.h"
#include "pc.h"
  
struct session *Sessions;
struct session *Command;
struct session *Current;
struct session *Lastcurr;
extern struct session *Trace;
int Row;
int Morewait;
extern int Numrows;
extern int StatusLines;
  
char Notval[] = "Not a valid control block\n";
static char Badsess[] = "Invalid session\n";
char TooManySessions[] = "Too many sessions\n";
  
char *Sestypes[] = {
    "Command",
    "Telnet",
    "FTP",
    "AX25",
    "Finger",
    "Ping",
    "NET/ROM",
    "Command",
    "More",
    "Hopcheck",
    "Tip",
    "PPP PAP",
    "Dial",
    "Query",
    "Cache",
    "Rlogin",
    "Repeat",
    "Look",
    "Trace"
};
  
/* Convert a character string containing a decimal session index number
 * into a pointer. If the arg is NULLCHAR, use the current default session.
 * If the index is out of range or unused, return NULLSESSION.
 */
struct session *
sessptr(cp)
char *cp;
{
    struct session *sp;
    unsigned int i;
  
    if(cp == NULLCHAR){
        sp = Lastcurr;
    } else {
        i = (unsigned)atoi(cp);
        if(i >= Nsessions)
            sp = NULLSESSION;
        else
            sp = &Sessions[i];
    }
    if(sp == NULLSESSION || sp->type == FREE)
        sp = NULLSESSION;
  
    return sp;
}
  
/* Select and display sessions */
int
dosession(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct session *sp;
    struct sockaddr fsocket;
    int i,k,s;
    int r,t;
    char *cp,swap;
    char *param[3];
  
    sp = (struct session *)p;
  
    if(argc > 1){
        if(strncmpi(argv[1],"swap",strlen(argv[1])) == 0) {
            if(argc == 2) {
#ifdef EMS
                if(SwapMode == EMS_SWAP)
                    tputs("EMS\n");
                else
#endif
#ifdef XMS
                    if(SwapMode == XMS_SWAP)
                        tputs("XMS\n");
                    else
#endif
                        if(SwapMode == MEM_SWAP)
                            tputs("Memory\n");
                        else
                            tputs("Tmp files\n");
            } else {
#ifdef EMS
                if(*argv[2] == 'e' || *argv[2] == 'E') {
                    SwapMode = EMS_SWAP;
                } else
#endif
#ifdef XMS
                    if(*argv[2] == 'x' || *argv[2] == 'X') {
                        if(!ScreenSizeK)
                            ScreenSizeK = (ScreenSize / 1024) + 1;
                        SwapMode = XMS_SWAP;
                    } else
#endif
                        if(*argv[2] == 'f' || *argv[2] == 'F') {
                            SwapMode = FILE_SWAP;
                        } else {
                            SwapMode = MEM_SWAP;
                        }
            }
            return 0;
        }
        if((sp = sessptr(argv[1])) == NULLSESSION){
            tprintf("Session %s not active\n",argv[1]);
            return 1;
        }
        if(argc == 2){
            go(0,NULL,sp);
        } else {
            switch (*argv[2]) {
                case 'f':   /* flowmode */
                    param[0] = argv[2];
                    param[1] = argv[3];
                    param[2] = NULL;
                    setbool(&sp->flowmode,"Set flowmode on/off",argc-2,param);
                    break;
                default:
                    tprintf("usage:session # [flow [on/off]]\n");
            }
        }
        return 0;
    }
    tputs(" #  S#  Sw Type     Rcv-Q Snd-Q State        Remote socket\n");
    for(sp=Sessions; sp < &Sessions[Nsessions];sp++){
        if(sp->type == FREE || sp->type == COMMAND || sp->type == TRACESESSION)
            continue;
  
        /* Rcv-Q includes output pending at the screen driver */
        r = socklen(sp->output,1);
        t = 0;
        cp = NULLCHAR;
        if((s = sp->s) != -1){
            i = SOCKSIZE;
/*            s = sp->s; */
            k = getpeername(s,(char *)&fsocket,&i);
            r += socklen(s,0);
            t = socklen(s,1);
            cp = sockstate(s);
        }
#ifdef EMS
        if(sp->screen->stype == EMS_SWAP)
            swap = 'E';
        else
#endif
#ifdef XMS
            if(sp->screen->stype == XMS_SWAP)
                swap = 'X';
            else
#endif
                if(sp->screen->stype == FILE_SWAP)
                    swap = 'F';
                else swap = 'M';
  
        tputc((Lastcurr == sp)?'*':' ');
        tprintf("%-3u", (unsigned)(sp - Sessions));
        tprintf("%-4d%c  %-8s%6d%6d %-13s",
        s,swap,Sestypes[sp->type],r,t,(cp != NULLCHAR) ? cp : "Limbo!");
        if(sp->name != NULLCHAR)
            tprintf("%s ",sp->name);
        if(sp->s != -1 && k == 0)
            tprintf("(%s)",psocket(&fsocket));
        tputc('\n');
  
        if(sp->type == FTP && (s = sp->cb.ftp->data) != -1){
            /* Display data channel, if any */
            i = SOCKSIZE;
            k = getpeername(s,(char *)&fsocket,&i);
            r = socklen(s,0);
            t = socklen(s,1);
            cp = sockstate(s);
            tprintf("    %-4d   %-8s%6d%6d %-13s%s",
            s,Sestypes[sp->type],r,t,
            (cp != NULLCHAR) ? cp : "Limbo!",
            (sp->name != NULLCHAR) ? sp->name : "");
            if(k == 0)
                tprintf(" (%s)",psocket(&fsocket));
            if(tputc('\n') == EOF)
                break;
        }
        if(sp->rfile != NULLCHAR)
            tprintf("    Record: %s\n",sp->rfile);
        if(sp->ufile != NULLCHAR)
            tprintf("    Upload: %s\n",sp->ufile);
    }
    return 0;
}
/* Resume current session, and wait for it */
int
go(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct session *sp;
  
    sp = (struct session *)p;
    if(sp == NULLSESSION || sp->type == FREE || \
        sp->type == COMMAND || sp->type == TRACESESSION)
        return 0;
    Current = sp;
    swapscreen(Command,sp);
#ifdef STATUSWIN
    UpdateStatus();
#endif
    psignal(sp,0);
    return 0;
}
int
doclose(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct session *sp;
  
    sp = (struct session *)p;
    if(argc > 1)
        sp = sessptr(argv[1]);
  
    if(sp == NULLSESSION){
        tputs(Badsess);
        return -1;
    }
    shutdown(sp->s,1);
    if(sp->type == MORE || sp->type == LOOK)
        alert(sp->proc,EABORT);
    return 0;
}
int
doreset(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct session *sp;
  
    sp = (struct session *)p;
    if(argc > 1)
        sp = sessptr(argv[1]);
  
    if(sp == NULLSESSION){
        tputs(Badsess);
        return -1;
    }
    /* Unwedge anyone waiting for a domain resolution, etc */
    alert(sp->proc,EABORT);
    shutdown(sp->s,2);
    if(sp->type == FTP)
        shutdown(sp->cb.ftp->data,2);
    return 0;
}
int
dokick(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct session *sp;
  
    sp = (struct session *)p;
    if(argc > 1)
        sp = sessptr(argv[1]);
  
    if(sp == NULLSESSION){
        tputs(Badsess);
        return -1;
    }
    sockkick(sp->s);
    if(sp->type == FTP)
        sockkick(sp->cb.ftp->data);
    return 0;
}
  
struct session *
newsession(name,type,split)
char *name;
int type;
int split;
{
    struct session *sp;
    int i;
  
    /* Make sure we have enough memory to swap the old session out - WG7J */
    if(availmem() < Memthresh/2 + ScreenSize)
        return NULLSESSION;
  
    /* Reserve the highest session for Trace, so that
     * the F-key session switching works correctly - WG7J
     */
    if(type == TRACESESSION) { /* This can only get called once !! */
        i=0;
        sp=Sessions;
        while(i!=Nsessions-1) {
            i++;
            sp++;
        }
    } else {
        for(i=0,sp=Sessions;i < Nsessions;sp++,i++)
            if(sp->type == FREE)
                break;
    }
    if(i == Nsessions)
        return NULLSESSION;
  
    sp->curdirs = NULL;
    sp->num = i;
    sp->type = type;
    sp->s = -1;
    if(name != NULLCHAR)
        sp->name = strdup(name);
    sp->proc = Curproc;
    /* Create standard input and output sockets. Output is
     * translated to local end-of-line by default
     */
    if(type != TRACESESSION) {
        close_s(Curproc->input);
        Curproc->input =  sp->input = socket(AF_LOCAL,SOCK_STREAM,0);
        seteol(Curproc->input,Eol);
        sockmode(Curproc->input,SOCK_BINARY);
        close_s(Curproc->output);
        Curproc->output = sp->output = socket(AF_LOCAL,SOCK_STREAM,0);
        seteol(Curproc->output,Eol);
        sockmode(Curproc->output,SOCK_ASCII);
    } else
        sp->input = sp->output = -1;
#ifdef TRACE
    if(type == COMMAND && Trace) {
        Trace->input = sp->input;
        Trace->output = sp->output;
    }
#endif
  
    /* on by default */
    sp->ttystate.crnl = sp->ttystate.edit = sp->ttystate.echo = 1;
    sp->flowmode = 0;   /* Off by default */
    sp->row = Numrows - 1 - StatusLines;
    sp->morewait = 0;
    sp->split = split;
    newscreen(sp);
    swapscreen(Current,sp);
    Current = sp;
#ifdef STATUSWIN
    UpdateStatus();
#endif
    return sp;
}
void
freesession(sp)
struct session *sp;
{
    if(sp == NULLSESSION)
        return;
    pwait(NULL);    /* Wait for any pending output to go */
    rflush();
  
    if(sp->proc1 != NULLPROC)
        killproc(sp->proc1);
    sp->proc1 = NULLPROC;
    if(sp->proc2 != NULLPROC)
        killproc(sp->proc2);
    sp->proc2 = NULLPROC;
  
    free_p(sp->ttystate.line);
    sp->ttystate.line = NULLBUF;
    if(sp->s != -1)
        close_s(sp->s);
    if(sp->record != NULLFILE){
        fclose(sp->record);
        sp->record = NULLFILE;
    }
    free(sp->rfile);
    sp->rfile = NULLCHAR;
    if(sp->upload != NULLFILE){
        fclose(sp->upload);
        sp->upload = NULLFILE;
    }
    free(sp->ufile);
    sp->ufile = NULLCHAR;
    free(sp->name);
    sp->name = NULLCHAR;
    sp->type = FREE;
  
    close_s(sp->input);
    sp->input = -1;
    sp->proc->input = -1;
    close_s(sp->output);
    sp->output = -1;
    sp->proc->output = -1;
  
    freescreen(sp);
    if(Current == sp){
        Current = Command;
        swapscreen(NULLSESSION,Command);
#ifdef STATUSWIN
        UpdateStatus();
#endif
#ifndef LINUX
        alert(Display,1);
#endif
    }
    if(Lastcurr == sp)
        Lastcurr = NULLSESSION;
}
#ifdef ALLCMD
/* Control session recording */
int
dorecord(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    struct session *sp;
    char *mode;
  
    sp = (struct session *)p;
    if(sp == NULLSESSION){
        tputs("No current session\n");
        return 1;
    }
    if(argc > 1){
        if(sp->rfile != NULLCHAR){
            fclose(sp->record);
            free(sp->rfile);
            sp->record = NULLFILE;
            sp->rfile = NULLCHAR;
        }
        /* Open new record file, unless file name is "off", which means
         * disable recording
         */
        if(strcmp(argv[1],"off") != 0){
            if(sockmode(sp->output,-1) == SOCK_ASCII)
                mode = APPEND_TEXT;
            else
                mode = APPEND_BINARY;
  
            if((sp->record = fopen(argv[1],mode)) == NULLFILE)
                tprintf("Can't open %s: %s\n",argv[1],sys_errlist[errno]);
            else
                sp->rfile = strdup(argv[1]);
        }
    }
    if(sp->rfile != NULLCHAR)
        tprintf("Recording into %s\n",sp->rfile);
    else
        tputs("Recording off\n");
    return 0;
}
/* Control file transmission */
int
doupload(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    register struct session *sp;
  
    sp = (struct session *)p;
    if(sp == NULLSESSION){
        tputs("No current session\n");
        return 1;
    }
    if(argc < 2){
        if(sp->ufile != NULLCHAR)
            tprintf("Uploading %s\n",sp->ufile);
        else
            tputs("Uploading off\n");
        return 0;
    }
    if(strcmp(argv[1],"stop") == 0 && sp->upload != NULLFILE){
        /* Abort upload */
        fclose(sp->upload);
        sp->upload = NULLFILE;
        free(sp->ufile);
        sp->ufile = NULLCHAR;
        killproc(sp->proc2);
        sp->proc2 = NULLPROC;
        return 0;
    }
    /* Open upload file */
    if((sp->upload = fopen(argv[1],READ_TEXT)) == NULLFILE){
        tprintf("Can't read %s: %s\n",argv[1],sys_errlist[errno]);
        return 1;
    }
    sp->ufile = strdup(argv[1]);
    /* All set, invoke the upload process */
    sp->proc2 = newproc("upload",1024,upload,0,sp,NULL,0);
    return 0;
}
/* File uploading task */
void
upload(unused,sp1,p)
int unused;
void *sp1;
void *p;
{
    struct session *sp;
    int oldf;
    char *buf;
  
    sp = (struct session *)sp1;
  
    /* Disable newline buffering for the duration */
    oldf = setflush(sp->s,-1);
  
    buf = mallocw(BUFSIZ);
    while(fgets(buf,BUFSIZ,sp->upload) != NULLCHAR)
        if(usputs(sp->s,buf) == EOF)
            break;
  
    free(buf);
    usflush(sp->s);
    setflush(sp->s,oldf);
    fclose(sp->upload);
    sp->upload = NULLFILE;
    free(sp->ufile);
    sp->ufile = NULLCHAR;
    sp->proc2 = NULLPROC;
}
#endif /*ALLCMD*/
