/* POP3 Server state machine - see RFC 1225
 *
 *      Jan 92  Erik Olson olson@phys.washington.edu
 *              Taken from POP2 server code in NOS 910618
 *              Rewritten/converted to POP3
 *      Feb 92  William Allen Simpson
 *              integrated with current work
 *      Aug-Oct 92      Mike Bilow, N1BEE, mikebw@ids.net
 *              Extensive bug fixes; changed uses of Borland stat()
 *              to fsize() in order to fix intermittent crashes;
 *              corrected confusion of sockmode()
 *
 *  "Need-to" list: XTND XMIT (to get WinQVTnet to work)
 *
 *  Support for Mail Index Files, June/July 1993, Johan. K. Reinalda, WG7J
 *  LZW support by Dave Brown, N2RJT.
 */
  
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#ifdef UNIX
#include <sys/types.h>
#endif
#if     defined(__STDC__) || defined(__TURBOC__)
#include <stdarg.h>
#endif
#include <ctype.h>
#include <setjmp.h>
#include "global.h"
#ifdef POP3SERVER
#include "mbuf.h"
#include "cmdparse.h"
#include "socket.h"
#include "proc.h"
#include "files.h"
#include "smtp.h"
#include "dirutil.h"
#include "mailutil.h"
#include "bm.h"
#include "index.h"
#if defined(LZW)
#include "lzw.h"
#ifdef MAILCLIENT
extern int poplzw;
#else
static int poplzw = TRUE;   /* no way yet to reset this */
#endif
#endif
  
/* ---------------- common server data structures ---------------- */
/* POP message pointer element */
  
struct pop_msg {
    long len;
    long pos;
    int deleted;
    struct pop_msg *next;
};
  
/* POP server control block */
  
struct pop_scb {
    int socket;             /* socket number for this connection */
    char state;             /* server state */
#define      LSTN       0
#define      AUTH       1
#define      TRANS      2
#define      UPDATE     3
#define      DONE       5
  
    char    buf[TLINELEN];   /* input line buffer */
    char    count;          /* line buffer length */
    char    username[64];   /* user/folder name */
    FILE    *wf;            /* work folder file pointer */
    int     folder_len;     /* number of msgs in current folder */
    int     high_num;       /* highest message number accessed */
    long    folder_file_size; /* length of the current folder file, in bytes */
    char    folder_modified; /* mail folder contents modified flag */
    struct pop_msg *msg;    /* message database link-list */
};

#ifdef STATUSWIN
int PopUsers;
#endif
  
#define NULLSCB  (struct pop_scb *)0
  
/* Response messages -- '\n' is converted to '\r\n' by the socket code */
  
static char     count_rsp[]     = "+OK you have %d messages\n",
error_rsp[]     = "-ERR %s\n",
greeting_msg[]  = "+OK %s POP3 ready\n",
#if defined(LZW)
xlzw_rsp[]      = "+OK lzw %d %d\n",
#endif
user_rsp[]      = "+OK user\n",
stat_rsp[]      = "+OK %d %ld\n",
list_single_rsp[]       = "+OK %d %ld\n",
list_multi_rsp[]        = "+OK %d messages (%ld octets)\n",
retr_rsp[]      = "+OK %ld octets\n",
multi_end_rsp[] = ".\n",
dele_rsp[]      = "+OK message %d deleted\n",
noop_rsp[]      = "+OK\n",
last_rsp[]      = "+OK %d\n",
signoff_msg[]   = "+OK Bye, bye-bye, bye now, goodbye\n";
  
static struct pop_scb *create_scb __ARGS((void));
static void delete_scb __ARGS((struct pop_scb *scb));
static void popserv __ARGS((int s,void *unused,void *p));
static int poplogin __ARGS((char *pass,char *username));
  
static void pop_sm __ARGS((struct pop_scb *scb));
  
static int Spop = -1; /* prototype socket for service */
  
  
/* Start up POP receiver service */
int
pop3start(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    int16 port;
  
    if(argc < 2)
        port = IPPORT_POP3;
    else
        port = atoi(argv[1]);
  
    return start_tcp(port,"POP3 Server",popserv,1536);
}
  
/* Shutdown POP3 service (existing connections are allowed to finish) */
  
int
pop3stop(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    int16 port;
  
    if(argc < 2)
        port = IPPORT_POP3;
    else
        port = atoi(argv[1]);
    return stop_tcp(port);
}
  
static void
popserv(s,unused,p)
int s;
void *unused;
void *p;
{
    struct pop_scb *scb;
    char *cp;
  
    sockowner(s,Curproc);           /* We own it now */
    log(s,"open POP3");
#ifdef STATUSWIN
    PopUsers++;
#endif
  
    if((scb = create_scb()) == NULLSCB) {
        tputs(Nospace);
        log(s,"close POP3 - no space");
#ifdef STATUSWIN
	PopUsers--;
#endif
        close_s(s);
        return;
    }
  
    scb->socket = s;
    scb->state  = AUTH;
  
    sockmode(s,SOCK_ASCII);         /* N1BEE */
    (void) usprintf(s,greeting_msg,Hostname);
  
    loop:   if ((scb->count = recvline(s,scb->buf,TLINELEN)) == -1){
        /* He closed on us */
  
        goto quit;
    }
  
    rip(scb->buf);
    if (strlen(scb->buf) == 0)      /* Ignore blank cmd lines */
        goto loop;
  
    /* Convert lower, and mixed case commands to UPPER case - Ashok */
    for(cp = scb->buf;*cp != ' ' && *cp != '\0';cp++)
        *cp = toupper(*cp);
  
    pop_sm(scb);
    if (scb->state == DONE)
        goto quit;
  
    goto loop;
  
    quit:
    log(scb->socket,"close POP3");
    close_s(scb->socket);
#ifdef STATUSWIN
    PopUsers--;
#endif
    delete_scb(scb);
}
  
  
/* Create control block, initialize */
  
static struct
pop_scb *create_scb()
{
    register struct pop_scb *scb;
  
    if((scb = (struct pop_scb *)callocw(1,sizeof (struct pop_scb))) == NULLSCB)
        return NULLSCB;
  
    scb->username[0] = '\0';
    scb->msg = NULL;
    scb->wf = NULL;
  
    scb->count = scb->folder_file_size = 0;
  
    scb->folder_modified = FALSE;
    return scb;
}
  
  
/* Free msg link-list */
static void
delete_msglist(struct pop_msg *b_msg)
{
    struct pop_msg *msg,*msg2;
    msg=b_msg;
    while(msg!=NULL) {msg2=msg->next; free(msg); msg=msg2;}
}
  
/* Free resources, delete control block */
  
static void
delete_scb(scb)
register struct pop_scb *scb;
{
    if (scb == NULLSCB)
        return;
    if (scb->wf != NULL)
        fclose(scb->wf);
    if (scb->msg  != NULL)
        delete_msglist(scb->msg);
  
    free((char *)scb);
}
  
/* --------------------- start of POP server code ------------------------ */
  
#define BITS_PER_WORD   16
  
#define isSOM(x)        ((strncmp(x,"From ",5) == 0))
  
/* Command string specifications */
  
static char
user_cmd[] = "USER ",
pass_cmd[] = "PASS ",
quit_cmd[] = "QUIT",
stat_cmd[] = "STAT",
list_cmd[] = "LIST",
#if defined(LZW)
xlzw_cmd[] = "XLZW ",
#endif
retr_cmd[] = "RETR",
dele_cmd[] = "DELE",
noop_cmd[] = "NOOP",
rset_cmd[] = "RSET",
top_cmd[]  = "TOP",
last_cmd[] = "LAST";
  
static void
pop_sm(scb)
struct pop_scb *scb;
{
    char password[40];
#if defined(LZW)
    int lzwmode, lzwbits;
    extern int16 Lzwbits;
    extern int Lzwmode;
#endif
  
#ifndef __TURBOC__
    static
#endif
    void state_error(struct pop_scb *,char *);
#ifndef __TURBOC__
    static
#endif
    void fatal_error(struct pop_scb *,char *);
#ifndef __TURBOC__
    static
#endif
    void open_folder(struct pop_scb *);
#ifndef __TURBOC__
    static
#endif
    void do_cleanup(struct pop_scb *);
#ifndef __TURBOC__
    static
#endif
    void stat_message(struct pop_scb *);
#ifndef __TURBOC__
    static
#endif
    void list_message(struct pop_scb *);
#ifndef __TURBOC__
    static
#endif
    void retr_message(struct pop_scb *);
#ifndef __TURBOC__
    static
#endif
    void dele_message(struct pop_scb *);
#ifndef __TURBOC__
    static
#endif
    void noop_message(struct pop_scb *);
#ifndef __TURBOC__
    static
#endif
    void last_message(struct pop_scb *);
#ifndef __TURBOC__
    static
#endif
    void rset_message(struct pop_scb *);
#ifndef __TURBOC__
    static
#endif
    void top_message(struct pop_scb *);
#ifndef __TURBOC__
    static
#endif
    void close_folder(struct pop_scb *);
  
    if (scb == NULLSCB) /* be certain it is good -- wa6smn */
        return;
  
#if defined(LZW)
    if (strncmp(scb->buf,xlzw_cmd,strlen(xlzw_cmd)) == 0){
        sscanf(scb->buf,"XLZW %d %d", &lzwbits,&lzwmode);
        if((lzwmode == 0 || lzwmode == 1)
           && (lzwbits > 8 && lzwbits < 17) && poplzw) {
               (void) usprintf(scb->socket,xlzw_rsp,lzwbits,lzwmode);
               lzwinit(scb->socket,lzwbits,lzwmode);
        } else if (!poplzw)
            state_error(scb,"(AUTH) LZW compression disabled");
        else
            state_error(scb,"LZW bits or mode invalid");
        return;
    }
#endif

    switch(scb->state) {
  
        case AUTH:
            if (strncmp(scb->buf,user_cmd,strlen(user_cmd)) == 0){
                sscanf(scb->buf,"USER %s",scb->username);
                (void) usputs(scb->socket,user_rsp);
  
            } else if (strncmp(scb->buf,pass_cmd,strlen(pass_cmd)) == 0){
                sscanf(scb->buf,"PASS %s",password);
  
                if (!poplogin(scb->username,password)) {
                    log(scb->socket,"POP3 access DENIED to %s",
                    scb->username);
                    state_error(scb,"Access DENIED!!");
                    return;
                }
  
                log(scb->socket,"POP3 access granted to %s",
                scb->username);
                open_folder(scb);
            } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
                do_cleanup(scb);
            } else
                state_error(scb,"(AUTH) expected USER, PASS or QUIT");
            break;
  
        case TRANS:
            if (strncmp(scb->buf,stat_cmd,strlen(stat_cmd)) == 0)
                stat_message(scb);
  
            else if (strncmp(scb->buf,list_cmd,strlen(list_cmd)) == 0)
                list_message(scb);
  
            else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
                retr_message(scb);
  
            else if (strncmp(scb->buf,dele_cmd,strlen(dele_cmd)) == 0)
                dele_message(scb);
  
            else if (strncmp(scb->buf,noop_cmd,strlen(noop_cmd)) == 0)
                noop_message(scb);
  
            else if (strncmp(scb->buf,last_cmd,strlen(last_cmd)) == 0)
                last_message(scb);
  
            else if (strncmp(scb->buf,top_cmd,strlen(top_cmd)) == 0)
                top_message(scb);
  
            else if (strncmp(scb->buf,rset_cmd,strlen(rset_cmd)) == 0)
                rset_message(scb);
  
            else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
                do_cleanup(scb);
  
            else
                state_error(scb,
                "(TRANS) unsupported/unrecognized command");
            break;
  
        case DONE:
            break;
  
        default:
            fatal_error(scb,"(TOP) State Error!!");
            break;
    }
}
  
static void
do_cleanup(scb)
struct pop_scb *scb;
{
#ifndef __TURBOC__
    static
#endif
    void close_folder(struct pop_scb *);
  
    close_folder(scb);
    (void) usputs(scb->socket,signoff_msg);
    scb->state = DONE;
}
  
static void
state_error(scb,msg)
struct pop_scb *scb;
char *msg;
{
    (void) usprintf(scb->socket,error_rsp,msg);
    /* scb->state = DONE; */  /* Don't automatically hang up */
}
  
static void
fatal_error(scb,msg)
struct pop_scb *scb;
char *msg;
{
    (void) usprintf(scb->socket,error_rsp,msg);
    scb->state = DONE;
}
  
static void
close_folder(scb)
struct pop_scb *scb;
{
    char folder_pathname[FILE_PATH_SIZE];
    char line[TLINELEN];
    FILE *fd;
    int deleted = FALSE;
    int msg_no = 0;
    int prevtype = NOHEADER;
    struct pop_msg *msg;
  
    int prev;
    long start = 0L;
    char *cp;
    struct mailindex ind;
  
#ifndef __TURBOC__
    static
#endif
    int newmail(struct pop_scb *);
#ifndef __TURBOC__
    static
#endif
    void state_error(struct pop_scb *,char *);
#ifndef __TURBOC__
    static
#endif
    void fatal_error(struct pop_scb *,char *);
  
  
    if (scb->wf == NULL)
        return;
  
    if (!scb->folder_modified) {
        /* no need to re-write the folder if we have not modified it */
  
        fclose(scb->wf);
        scb->wf = NULL;
  
        delete_msglist(scb->msg);
        scb->msg=NULL;
        return;
    }
  
  
    sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
    if(mlock(Mailspool,scb->username)) {
        fatal_error(scb,"Can't lock the mail folder");
        return;
    }
  
    if (newmail(scb)) {
        /* copy new mail into the work file and save the
                    message count for later */
  
        if ((fd = fopen(folder_pathname,"r")) == NULL) {
            fatal_error(scb,"Unable to add new mail to folder");
            rmlock(Mailspool,scb->username);
            return;
        }
  
        fseek(scb->wf,0,SEEK_END);
        fseek(fd,scb->folder_file_size,SEEK_SET);
        while (!feof(fd)) {
            fgets(line,TLINELEN,fd);
            fputs(line,scb->wf);
        }
  
        fclose(fd);
    }
  
    /* now create the updated mail folder */
  
    if ((fd = fopen(folder_pathname,"w")) == NULL){
        fatal_error(scb,"Unable to update mail folder");
        rmlock(Mailspool,scb->username);
        return;
    }
  
    /* This simply rewrites the whole mail folder,
     * so also simply recreate the index file - WG7J
     */
    delete_index(scb->username);
    memset(&ind,0,sizeof(struct mailindex));
  
    rewind(scb->wf);
    msg=scb->msg;
    prev = 0;
    while(fgets(line,TLINELEN,scb->wf) != NULLCHAR) {
        pwait(NULL);
        if (isSOM(line)){
            if(prev && !deleted) {
                /* write the index for the previous message */
                ind.size = ftell(fd) - start;
                write_index(scb->username,&ind);
            } else
                prev = 1;
            if (msg!=NULL)
                msg=msg->next;
            msg_no++;
            if (msg!=NULL)
                deleted = msg->deleted;
            else
                deleted = FALSE;
            if(!deleted) {
                /* read the smtp header first to create the index */
                start = ftell(fd);  /* Starting offset of this message */
                fputs(line,fd);     /* put the from line back */
                default_index(scb->username,&ind);
  
                /* read the 'Received...' and 'ID... lines'
                 * to get the msgid
                 */
                fgets(line,TLINELEN,scb->wf);
                fputs(line,fd);
                fgets(line,TLINELEN,scb->wf);
                fputs(line,fd);
                if((cp=strstr(line,"AA")) != NULLCHAR)
                    /*what follows is the message-number*/
                    ind.msgid = atol(cp+2);
  
                /* now scan rest of headers */
                while(fgets(line,TLINELEN,scb->wf) != NULLCHAR) {
                    fputs(line,fd);
                    if(*line == '\n')
                        break; /* end of headers */
                    set_index(line,&ind,htype(line,&prevtype));
                }
            }
        } else if(!deleted) {
  
            fputs(line,fd);
        }
    }
  
    /* Update the last message handled */
    if(prev && !deleted) {
        ind.size = ftell(fd) - start;
        write_index(scb->username,&ind);
    }
  
    fclose(fd);
    if(fsize(folder_pathname) == 0L)                /* N1BEE */
        unlink(folder_pathname);
    rmlock(Mailspool,scb->username);
    fclose(scb->wf);
    scb->wf = NULL;
    delete_msglist(scb->msg);
    scb->msg=NULL;
}
  
static void
open_folder(scb)
struct pop_scb *scb;
{
    char folder_pathname[FILE_PATH_SIZE];
    char line[TLINELEN];
    long pos;
    FILE *fd;
    FILE *tmpfile __ARGS((void));
    struct stat folder_stat;
    struct pop_msg *msg;
  
    sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
    scb->folder_len       = 0;
    scb->folder_file_size = 0;
    /* if (stat(folder_pathname,&folder_stat)){ */
    if((folder_stat.st_size = fsize(folder_pathname)) == -1) { /* N1BEE */
        (void) usprintf(scb->socket,count_rsp,scb->folder_len);
        scb->state  = TRANS;
        return;         /* no file = OK */
    }
  
    scb->folder_file_size = folder_stat.st_size;
    if ((fd = fopen(folder_pathname,"r")) == NULL){
        state_error(scb,"Unable to open mail folder");
        return;
    }
  
    if ((scb->wf = tmpfile()) == NULL) {
        state_error(scb,"Unable to create work folder");
        return;
    }
  
    scb->msg=calloc(sizeof(struct pop_msg),1); /* create first element */
    if (scb->msg==NULL)
    {
        fatal_error(scb,"Unable to create pointer list");
        return;
    }
    scb->msg->next=NULL;
    msg=scb->msg;
    msg->len=0;
    msg->deleted=0;
  
    while(!feof(fd)) {
        pos=ftell(scb->wf);
        fgets(line,TLINELEN,fd);
  
        /* scan for begining of a message */
  
        if (isSOM(line))
        {
            pwait(NULL);
            scb->folder_len++;
            msg->next=calloc(sizeof(struct pop_msg),1);
            if (msg->next==NULL)
            {
                fatal_error(scb,
                "Unable to create pointer list");
                return;
            }
            msg=msg->next;
            msg->pos=pos;
            msg->next=NULL;
            msg->len=0;
            msg->deleted=0;
  
        /* now put  the line in the work file */
        }
        fputs(line,scb->wf);
        rip(line);
        if ( *line == '.' ) msg->len++;
        msg->len +=strlen(line)+2; /* Add msg len count */
    }
  
    fclose(fd);
    scb->high_num=0;  /* reset high read */
  
    (void) usprintf(scb->socket,count_rsp,scb->folder_len);
  
    scb->state  = TRANS;
}
  
static void
stat_message(scb)
struct pop_scb *scb;
{
    long total=0;
    int count=0;
    struct pop_msg *msg;
  
    if (scb == NULLSCB) /* check for null -- wa6smn */
        return;
  
    if (scb->folder_len)    /* add everything up */
        for (msg=scb->msg->next; msg!=NULL; msg=msg->next)
            if (!msg->deleted)
                {       total += msg->len; ++count;}
  
    (void) usprintf(scb->socket,stat_rsp,count,total);
}
  
static void
list_message(scb)
struct pop_scb *scb;
{
    struct pop_msg *msg;
    int msg_no=0, oldf;
    long total=0;
#ifndef __TURBOC__
    static
#endif
    struct pop_msg *goto_msg(struct pop_scb *,int );
  
    if (scb == NULLSCB) /* check for null -- wa6smn */
        return;
    if (scb->buf[sizeof(list_cmd) - 1] == ' ')
    {
        msg_no = atoi(&(scb->buf[sizeof(list_cmd) - 1]));
        msg=goto_msg(scb,msg_no);
        if (msg==NULL || msg->deleted)
            state_error(scb,"non existent or deleted message");
        else
            (void) usprintf(scb->socket,list_single_rsp,
            msg_no,msg->len);
    } else  /* multiline */
    {
        if (scb->folder_len)            /* add everything */
            for (msg=scb->msg->next; msg!=NULL;msg=msg->next)
                if (!msg->deleted)
                    total += msg->len,++msg_no;
  
        oldf = setflush(scb->socket,-1); /* we will flush it explicitly */
        (void) usprintf(scb->socket,list_multi_rsp,msg_no,total);
  
        if (scb->folder_len)
            for (msg=scb->msg->next,msg_no=1; msg!=NULL;
                msg=msg->next,msg_no++)
                if (!msg->deleted) {
                    (void) usprintf(scb->socket,"%d %ld\n",
                    msg_no,msg->len);
                }
        (void) usputs(scb->socket,multi_end_rsp);
        usflush(scb->socket);
        setflush(scb->socket,oldf);
    }
}
  
static void
retr_message(scb)
struct pop_scb *scb;
{
    char line[TLINELEN];
    long cnt;
    int msg_no, oldf;
    struct pop_msg *msg;
#ifndef __TURBOC__
    static
#endif
    struct pop_msg *goto_msg(struct pop_scb *,int );
  
    if (scb == NULLSCB) /* check for null -- wa6smn */
        return;
    if (scb->buf[sizeof(retr_cmd) - 1] != ' ')
    {
        state_error(scb,"no such message");
        return;
    }
    msg_no = atoi(&(scb->buf[sizeof(retr_cmd) - 1]));
    msg=goto_msg(scb,msg_no);
    if (msg==NULL || msg->deleted) {
        state_error(scb,"no such message");
        return;
    }
  
    cnt  = msg->len;
    oldf = setflush(scb->socket,-1); /* we will flush it explicitly */
    (void) usprintf(scb->socket,retr_rsp,cnt);
    fseek(scb->wf,msg->pos,SEEK_SET);  /* Go there */
  
    while(!feof(scb->wf) && (cnt > 0)) {
        fgets(line,TLINELEN,scb->wf);
        rip(line);
        if ( *line == '.' ) {
            (void) usputc(scb->socket,'.');
            cnt--;
        }
        (void) usputs(scb->socket,line);
        (void) usputc(scb->socket,'\n');
        cnt -= (strlen(line)+2); /* Compensate for CRLF */
    }
    (void) usputs(scb->socket,".\n");
    usflush(scb->socket);
    setflush(scb->socket,oldf);
    if (msg_no >= scb->high_num)
        scb->high_num=msg_no;     /* bump high water mark */
}
  
static void
noop_message(scb)
struct pop_scb *scb;
{
    (void) usputs(scb->socket,noop_rsp);
}
  
static void
last_message(scb)
struct pop_scb *scb;
{
    (void) usprintf(scb->socket,last_rsp,scb->high_num);
}
  
static void
rset_message(scb)
struct pop_scb *scb;
{
    struct pop_msg *msg;
    long total=0;
  
    if (scb->folder_len)
        for (msg=scb->msg->next; msg!=NULL; msg=msg->next)
            msg->deleted=FALSE,total+=msg->len;
  
    scb->high_num=0;  /* reset last */
    scb->folder_modified=FALSE;
    (void) usprintf(scb->socket,list_multi_rsp,scb->folder_len,total);
}
  
static void
top_message(scb)
struct pop_scb *scb;
{
    char *ptr;
    char line[TLINELEN];
    struct pop_msg *msg;
    int msg_no=0,lines=0,oldf;
    long total=0;
  
#ifndef __TURBOC__
    static
#endif
    struct pop_msg *goto_msg(struct pop_scb *,int );
  
    if (scb == NULLSCB) /* check for null -- wa6smn */
        return;
    if (scb->buf[sizeof(top_cmd) - 1] != ' ')
    {
        state_error(scb,"No message specified");
        return;
    }
    for (ptr=scb->buf+sizeof(top_cmd); *ptr==' ' ; ++ptr);
        /* Space drop */
    for ( ; *ptr!=' ' && *ptr !='\0'; ++ptr);
        /* token drop */
    msg_no = atoi(&(scb->buf[sizeof(top_cmd) - 1]));
    lines = atoi(++ptr);  /* Get # lines to top */
    if (lines < 0) lines=0;
  
    msg=goto_msg(scb,msg_no);
    if (msg==NULL || msg->deleted)
    {
        state_error(scb,"non existent or deleted message");
        return;
    }
    fseek(scb->wf,msg->pos,SEEK_SET);  /* Go there */
    total=msg->len;  /* Length of current message */
    oldf = setflush(scb->socket,-1); /* we will flush it explicitly */
    (void) usputs(scb->socket,noop_rsp);  /* Give OK */
    do {
        fgets(line,TLINELEN,scb->wf);
        rip(line);
        if ( *line == '.' ) {
            (void) usputc(scb->socket,'.');
            total--;
        }
        total -= strlen(line)+2;
        (void) usputs(scb->socket,line);
        (void) usputc(scb->socket,'\n');
    } while (*line!='\0' && total>0);
    for ( ; total > 0 && lines; --lines) {
        fgets(line,TLINELEN,scb->wf);
        rip(line);
        if ( *line == '.' ) {
            (void) usputc(scb->socket,'.');
            total--;
        }
        total -= strlen(line)+2;
        (void) usputs(scb->socket,line);
        (void) usputc(scb->socket,'\n');
    }
    (void) usputs(scb->socket,multi_end_rsp);
    usflush(scb->socket);
    setflush(scb->socket,oldf);
}
  
static int
poplogin(username,pass)
char *pass;
char *username;
{
    char buf[80];
    char *cp;
    char *cp1;
    FILE *fp;
  
    if((fp = fopen(Popusers,"r")) == NULLFILE) {
        /* User file doesn't exist */
        tprintf("POP users file %s not found\n",Popusers);
        return(FALSE);
    }
  
    while(fgets(buf,sizeof(buf),fp),!feof(fp)) {
        if(buf[0] == '#')
            continue; /* Comment */
  
        if((cp = strchr(buf,':')) == NULLCHAR)
            /* Bogus entry */
            continue;
  
        *cp++ = '\0';  /* Now points to password */
        if(strcmp(username,buf) == 0)
            break;  /* Found user name */
    }
  
    if(feof(fp)) {
        /* User name not found in file */
  
        fclose(fp);
        return(FALSE);
    }
    fclose(fp);
  
    if ((cp1 = strchr(cp,':')) == NULLCHAR)
        return(FALSE);
  
    *cp1 = '\0';
    if(strcmp(cp,pass) != 0) {
        /* Password required, but wrong one given */
  
        return(FALSE);
    }
  
    /* whew! finally made it!! */
  
    return(TRUE);
}
  
static void
dele_message(scb)
struct pop_scb *scb;
{
    struct pop_msg *msg;
    int msg_no;
#ifndef __TURBOC__
    static
#endif
    struct pop_msg *goto_msg(struct pop_scb *,int );
  
    if (scb == NULLSCB) /* check for null -- wa6smn */
        return;
    if (scb->buf[sizeof(retr_cmd) - 1] != ' ')
    {
        state_error(scb,"no such message");
        return;
    }
    msg_no = atoi(&(scb->buf[sizeof(retr_cmd) - 1]));
    msg=goto_msg(scb,msg_no);
    if (msg==NULL || msg->deleted) {
        state_error(scb,"attempt to access deleted message");
        return;
    }
    if (msg->deleted) /* Don't bother if already dead */
    {
        state_error(scb,"message already deleted");
        return;
    }
    msg->deleted=TRUE;
    scb->folder_modified = TRUE;
    (void) usprintf(scb->socket,dele_rsp,msg_no);
}
  
static int
newmail(scb)
struct pop_scb *scb;
{
    char folder_pathname[FILE_PATH_SIZE];
    struct stat folder_stat;
  
    sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  
    /* if (stat(folder_pathname,&folder_stat)) { */
    if((folder_stat.st_size = fsize(folder_pathname)) == -1) { /* N1BEE */
        state_error(scb,"Unable to get old mail folder's status");
        return(FALSE);
    } else
        return ((folder_stat.st_size > scb->folder_file_size)? TRUE:FALSE);
}
  
static struct pop_msg *
goto_msg(struct pop_scb *scb,int msg_no)
{
    int msg_num;
    struct pop_msg *msg;
  
    msg_num=msg_no-1;
    if (scb->folder_len==0 || msg_num < 0)
        return NULL;
    for (msg=scb->msg->next; msg_num && msg!=NULL; --msg_num) msg=msg->next;
    return msg;
}
  
#endif
  
