/*
 *  AnyMail -x MailReadyFileName -c Command -e user -i user ...
 *
 *  (C) Copyright 1990 by Chris Hind Genly, chris@genly.uucp
 *
 *  See AnyMail.doc for legal terms.
 *
 */

#include <exec/types.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <libraries/dos.h>
#include <intuition/intuition.h>
#include <graphics/gfxbase.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "protos.h"
#include "config.h"
#include "version.h"

typedef struct line_tag {
    struct line_tag *Next;
    char             Msg[255];
} *LINE, LINE_STRUCT;

LINE Lines, LastLine;

struct Window	*Window;
struct MsgPort	*Port;
int              Count;

struct IntuitionBase *IntuitionBase;
struct GfxBase	     *GfxBase;

typedef struct MailNode {
    struct MailNode *Next;
    struct MailNode *Prev;
    char             Name[1];
} *MAILNODE, MAILNODE_STRUCT;

int
OkToScan(
    struct FileInfoBlock *fib
);

void
MakeNode(
    struct List *List,
    char *Name
);

struct NewWindow Nw = {
    40, 10, 560, 180, -1, -1,
    CLOSEWINDOW|REFRESHWINDOW|MOUSEBUTTONS,
    WINDOWCLOSE|WINDOWDRAG|WINDOWSIZING|SIMPLE_REFRESH|WINDOWDEPTH,
    NULL, NULL, 
    "AnyMail Version 1.0     You have mail", 
    NULL, /* Screen */
    NULL, /* BitMap */
    30, 30, 640, 200, WBENCHSCREEN
};


#define PORTNAME "AnyMail-Port"

int
Init(
    void
);

int
AnotherAnyMail(
    void
);

void
AnyMail(
    char *User
);

void
Display(
    void
);

int
TimeToRepeat(
    int *DoCommand
);

char *
ExtractPersonalName(
    char *str
);

void
Terminate(
    void
);

LINE 
AppendNewLine(
    void
);

void
DeleteList(
    void
);

char *
FromName(
     char *FromLine
);

struct List Excludes;
struct List Includes;

struct FileInfoBlock fib;
char *MailRdyName;

void
main(
    int argc,
    char **argv
)
{
    int i;
    BPTR uumail;
    char *Command = 0;
    int DoCommand = FALSE;
    
    NewList(&Excludes);
    NewList(&Includes);
 
    for(i=1; i<argc; ++i) {
        if (stricmp(argv[i], "-x") == 0) {
            MailRdyName = argv[++i];
        } else 
        if (stricmp(argv[i], "-e") == 0) {
            MakeNode(&Excludes, argv[++i]);
        } else
        if (stricmp(argv[i], "-i") == 0) {
            MakeNode(&Includes, argv[++i]);
        } else
        if (stricmp(argv[i], "-c") == 0) {
            Command = argv[++i];
        } else {
            fprintf(stderr, "AnyMail: Unknown keyword %s\n", argv[i]);
        }
    }
    
    if (MailRdyName)
        unlink(MailRdyName);
    
    if (AnotherAnyMail())
        exit(0);
        
    if (Init()) {
        
        while(TimeToRepeat(&DoCommand)) {

            DeleteList();
            
            uumail = Lock("uumail:", ACCESS_READ);
            if (Examine(uumail, &fib)) {
                while(ExNext(uumail, &fib)) {
                    if (!OkToScan(&fib)) continue;
                    
                    AnyMail(fib.fib_FileName);
                }
            }
            UnLock(uumail);
            
            Display();
        }
    }
    
    Terminate();
    
    if (DoCommand && Command) {
        Execute(Command, 0, 0);
    }
}

int
OkToScan(
    struct FileInfoBlock *fib
)
{
    MAILNODE n;
    char *FileName;
    
    if (fib->fib_DirEntryType >= 0) return FALSE;
                    
    for(n=(MAILNODE)Includes.lh_Head; n->Next; n = n->Next) {
        FileName = fib->fib_FileName;
        strlwr(FileName);
        if (astcsma(FileName, n->Name) == strlen(FileName)) {
            return TRUE;
        }
    }
                    
    for(n=(MAILNODE)Excludes.lh_Head; n->Next; n = n->Next) {
        FileName = fib->fib_FileName;
        strlwr(FileName);
        if (astcsma(FileName, n->Name) == strlen(FileName)) {
            return FALSE;
        }
    }
    
    return TRUE;
}

int
Init(
    void
)
{
	IntuitionBase = OpenLibrary("intuition.library", 0);
    if (IntuitionBase == 0) {
        fprintf(stderr, "AnyMail: Unable to open intuition.library\n");
        return 0;
    }
    
	GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0);
    if (GfxBase == 0) {
        fprintf(stderr, ": Unable to open graphics.library\n");
        return 0;
    }
    
    return 1;
}

int
AnotherAnyMail(
    void
)
{
    struct MsgPort *OtherPort;
    
    /*
     * Check for another port.  If none, create one.  Prevent races.
     */
    Forbid();
    OtherPort = FindPort(PORTNAME);
    if (OtherPort == NULL)
        Port = CreatePort(PORTNAME, 0);
    Permit();
    
    /*
     * Signal the other AnyMail to scan uumail: again.
     */
    if (OtherPort) {
        Signal(OtherPort->mp_SigTask, 1<<OtherPort->mp_SigBit);
    }
    
    return OtherPort != 0;
}   

int
TimeToRepeat(
    int *DoCommand
)
{
    int Done   = FALSE;
    int Repeat = FALSE;
    struct IntuiMessage *im;
    int WindowMask;
    int PortMask;
    int Signals;
    
    if (Window == 0) return TRUE;
    
    WindowMask = 1<<Window->UserPort->mp_SigBit;
    PortMask   = 1<<Port->mp_SigBit;
    
    while(!Done && !Repeat) {
    
        Signals = Wait(WindowMask|PortMask);
        
        if (Signals & WindowMask) {
            while (im = (struct IntuiMessage *)GetMsg(Window->UserPort)) {
                switch(im->Class) {
                case MOUSEBUTTONS:
                    if (im->Code == SELECTDOWN) {
                        *DoCommand = TRUE;
                        Done = TRUE;
                    }
                    break;
                case REFRESHWINDOW:
                    Display();
                    break;
                case CLOSEWINDOW:
                    Done = TRUE;
                    break;
                }
                ReplyMsg((struct Message *)im);
            }
        }
        if (Signals & PortMask) {
            Repeat = TRUE;
        }
    }
    
    return Repeat;
}

void
AnyMail(
    char *User
)
{
    static char Buf[256];
    static char FromLine[256];
    static char SubjLine[256];
    char *file = malloc(strlen(User) + 32);
    FILE *fi;
    long msgno = 0;
    LINE Line;
    int  NewUser = TRUE;

    if ('a' <= User[0] && User[0] <= 'z')
        User[0] = User[0] - 'a' + 'A';
        
    strcpy(file, MakeConfigPath(UUMAIL, User));
    if (fi = fopen(file, "r")) {
        while (fgets(Buf, 256, fi)) {

            /*
             *	Start of message
             */

            if (strncmp(Buf, "From ", 5) != 0)
                continue;

            ++msgno;

            /*
             *	Scan headers for From: and Subject:
             *	Headers end with a blank line.
             */

            FromLine[0] = 0;
            SubjLine[0] = 0;

            while (fgets(Buf, 256, fi) && Buf[0] != '\n') {
                if (strncmp(Buf, "From:", 5) == 0)
                    strcpy(FromLine, Buf + 5);
                if (strncmp(Buf, "Subject:", 8) == 0)
                    strcpy(SubjLine, Buf + 8);
            }

            /* Remove the trailing new line from the subject */
            if (SubjLine[0])  SubjLine[strlen(SubjLine)-1] = 0;
            
            if (NewUser) {
                NewUser = FALSE;
                if (Lines != 0) {
                    Line = AppendNewLine();
                    Line->Msg[0] = 0;
                }
                Line = AppendNewLine();
                strcpy(Line->Msg, "To ");
                strcat(Line->Msg, User);
            }
            
            Line = AppendNewLine();
            sprintf(Line->Msg,
                "%-2d %-20.20s  %s",
                msgno,
                FromName(FromLine),
                SubjLine
            );
            ++Count;
        }
        
        fclose(fi);
    }
}

void
Display(
    void
)
{
    int xmin, xmax, ymin, ymax, x, y;
    int len, lenmax;
    LINE Line;
    char Buff[200];

    if (Window == 0) {
        Window = OpenWindow(&Nw);
        if (Window == 0) 
            return;
            
        ScreenToFront(Window->WScreen);
    }
    
    xmin = Window->BorderLeft;             
    ymin = Window->BorderTop;
    xmax = Window->Width  - Window->BorderRight  - 1;
    ymax = Window->Height - Window->BorderBottom - 1;
    
    if (xmin < xmax && ymin < ymax) {
        SetAPen(Window->RPort, 0);
        RectFill(Window->RPort, xmin, ymin, xmax, ymax);
    }
    
    x = xmin + 2;
    y = ymin + 4;
    lenmax = (xmax-xmin+1)/GfxBase->DefaultFont->tf_XSize;
            
    SetAPen(Window->RPort, 1);
    SetBPen(Window->RPort, 0);
    
    for(Line=Lines; Line; Line = Line->Next) {
        if (y + GfxBase->DefaultFont->tf_YSize > ymax) break;
        len = strlen(Line->Msg);
        if (len > lenmax) len = lenmax;
        Move(Window->RPort, x, y+GfxBase->DefaultFont->tf_Baseline);
        Text(Window->RPort, Line->Msg, len);
        y += GfxBase->DefaultFont->tf_YSize;
    }
    
    sprintf(Buff, "AnyMail Version 1.0    You have %d message%s waiting.",
        Count, Count==1?"":"s");
    SetWindowTitles(Window, Buff, 0);
}

void
Terminate(
    void
)
{
    if (Window) 	   CloseWindow(Window);
    
    if (GfxBase)       CloseLibrary(GfxBase);
    if (IntuitionBase) CloseLibrary(IntuitionBase);
    
    if (Port)          DeletePort(Port);
}

void
DeleteList(
    void
)
{
    LINE Line, Next;
    
    for(Line = Lines; Line; Line = Next) {
        Next = Line->Next;
        free(Line);
    }
    Lines = 0;
    LastLine = 0;
    Count = 0;
}

            
LINE 
AppendNewLine(
    void
)
{
    LINE Line;
    
    Line = malloc(sizeof(LINE_STRUCT));
    Line->Next = 0;
    if (Lines == 0)
        Lines = Line;
    else 
        LastLine->Next = Line;
    LastLine = Line;
            
    return Line;
}

/*
 * The real name is between parens.  If it can't be found, use the
 * whole from line
 */
char *
FromName(
     char *FromLine
)
{
    char *Start;
    char *End;
    
    Start = strchr(FromLine, '(');
    if (Start) {
        Start++;
        End = strchr(Start, ')');
        if (End) {
            *End = 0;
            return Start;
        }
    }
    End = strchr(FromLine, '\n');
    if (End) *End = 0;
    
    return FromLine;
}

void
MakeNode(
    struct List *List,
    char *Name
)
{
    MAILNODE n;
    
    n = malloc(sizeof(MAILNODE_STRUCT) + strlen(Name));
    strcpy(n->Name, Name);
    
    AddTail(List, (struct Node *)n);
}
