/*
 * MultiMail offline mail reader
 * Blue Wave class

 Copyright 1996-1997 Toth Istvan <stoty@vma.bme.hu>
 Copyright 1998-2019 William McBrine <wmcbrine@gmail.com>
 Distributed under the GNU General Public License, version 3 or later. */

#include "bw.h"
#include "compress.h"

// -----------------------------------------------------------------
// The Blue Wave methods
// -----------------------------------------------------------------

bluewave::bluewave(mmail *mmA) : pktbase(mmA)
{
    persNdx = 0;

    findInfBaseName();

    initInf();

    ftiFile = openFile("FTI");

    initMixID();

    body = new bodytype *[maxConf];

    infile = openFile("DAT");
}

bluewave::~bluewave()
{
    if (!hasPers) {
        areas--;
        mixID--;
    }
    while (maxConf--)
        delete[] body[maxConf];

    delete[] mixID;
    delete[] areas;
    delete[] mixRecord;
    delete[] persNdx;

    fclose(ftiFile);
}

bool bluewave::hasPersonal()
{
    return true;
}

area_header *bluewave::getNextArea()
{
    int x, totmsgs, numpers;
    unsigned flags_raw;
    unsigned long flags_cooked;
    bool isPers = hasPers && !ID;

    x = mixID[ID];

    if (isPers)
        totmsgs = numpers = personal;
    else
        if (x != -1) {
            totmsgs = getshort(mixRecord[x].totmsgs);
            numpers = getshort(mixRecord[x].numpers);
        } else
            totmsgs = numpers = 0;

    body[ID] = totmsgs ? new bodytype[totmsgs] : 0;

    bool inet = isPers ? false : (areas[ID].network_type == INF_NET_INTERNET);

    flags_raw = getshort(areas[ID].area_flags);
    flags_cooked = isPers ? (PUBLIC | PRIVATE | COLLECTION | ACTIVE) :
        (hasOffConfig | SUBKNOWN | ((x != -1) ? ACTIVE : 0) |
        ((flags_raw & (INF_ALIAS_NAME | INF_ANY_NAME)) ? ALIAS : 0) |
        ((flags_raw & INF_NETMAIL) ? NETMAIL : 0) |
        (inet ? (INTERNET | LATINCHAR) : 0) |
        ((flags_raw & INF_ECHO) ? ECHOAREA : 0) |
        ((flags_raw & INF_NO_PUBLIC) ? 0 : PUBLIC) |
        ((flags_raw & INF_NO_PRIVATE) ? 0 : PRIVATE) |
        ((flags_raw & INF_PERSONAL) ? PERSONLY : 0) |
        ((flags_raw & INF_TO_ALL) ? PERSALL : 0) |
        ((flags_raw & INF_POST) ? 0 : READONLY));

    area_header *tmp = new area_header(mm, ID + 1, isPers ? "PERS" :
        (char *) areas[ID].areanum, isPers ? "PERSONAL" :
        (char *) areas[ID].echotag, (isPers ? "Letters addressed to you" :
        (areas[ID].title[0] ? (char *) areas[ID].title :
        (char *) areas[ID].echotag)), (isPers ? "Blue Wave personal" :
        "Blue Wave"), flags_cooked, totmsgs, numpers, from_to_len,
        inet ? 511 : subject_len);
    ID++;
    return tmp;
}

int bluewave::getNoOfLetters()
{
    int x = mixID[currentArea];
    bool isPers = hasPers && !currentArea;

    return isPers ? personal : ((x != -1) ?
        getshort(mixRecord[x].totmsgs) : 0);
}

letter_header *bluewave::getNextLetter()
{
    FTI_REC ftiRec;
    int areaID, letterID;

    if (currentLetter >= getNoOfLetters())
        return 0;

    if (hasPers && !currentArea) {
        areaID = persNdx[currentLetter].area;
        letterID = persNdx[currentLetter].msgnum;
    } else {
        areaID = currentArea;
        letterID = currentLetter;
    }

    fseek(ftiFile, getlong(mixRecord[mixID[areaID]].msghptr) +
          (long) letterID * ftiStructLen, SEEK_SET);

    if (!fread(&ftiRec, ORIGINAL_FTI_STRUCT_LEN, 1, ftiFile))
        fatalError("Error reading .FTI file");

    long msglength = getlong(ftiRec.msglength);

    body[areaID][letterID].pointer = getlong(ftiRec.msgptr);
    body[areaID][letterID].msgLength = msglength;

    cropesp((char *) ftiRec.from);
    cropesp((char *) ftiRec.to);
    cropesp((char *) ftiRec.subject);

    int flags = getshort(ftiRec.flags);

    net_address na;
    na.zone = getshort(ftiRec.orig_zone);
    if (na.zone) {
        na.net = getshort(ftiRec.orig_net);
        na.node = getshort(ftiRec.orig_node);
        na.point = 0;    // set from getBody()
        na.isSet = true;
    }

    currentLetter++;

    // Y2K precaution -- the date field may not be 0-terminated:
    ftiRec.date[19] = '\0';

    return new letter_header(mm, (char *) ftiRec.subject,
        (char *) ftiRec.to, (char *) ftiRec.from, (char *) ftiRec.date,
        0, getshort(ftiRec.replyto), letterID, getshort(ftiRec.msgnum),
        areaID, !(!(flags & MSG_NET_PRIVATE)), msglength, this, na,
        (areas[areaID].network_type == INF_NET_INTERNET));
}

void bluewave::getblk(int AreaID, long &offset, long blklen,
                      unsigned char *&p, unsigned char *&begin)
{
    bool internet, strip;

    internet = (areas[AreaID].network_type == INF_NET_INTERNET);
    strip = !internet && mm->resourceObject->getInt(StripSoftCR);

    for (long count = 0; count < blklen; count++) {
        int kar = fgetc(infile);

        // Skip leading space, if present:
        if (!count && (kar == ' '))
            kar = fgetc(infile);

        // Some buggy packets:
        if (!kar || (kar == EOF))
            kar = ' ';

        if (kar == '\r') {
            *p++ = '\n';
            begin = p;
            offset = ftell(infile);
        } else
            if ((kar != '\n') && (!strip || (kar != 0x8D)))
                *p++ = kar;
    }
}

void bluewave::endproc(letter_header &mhead)
{
    // Extra header info embedded in the text:

    const char *s;
    char *end;

    net_address &na = mhead.getNetAddr();
    int AreaID = mhead.getAreaID() - 1;

    if (areas[AreaID].network_type == INF_NET_INTERNET) {

        // Get address from "From:" line:
        if (!na.isSet) {
            s = getHidden("\001From: ", end);
            if (s) {
                na.isInternet = true;
                na = fromAddr(s);
                if (end)
                    *end = '\n';
            }
        }

        // Get Message-ID/References:
        if (!mhead.getMsgID()) {
            s = getHidden("\001Message-ID: ", end);
            if (s) {
                const char *s2;
                char *end2, *final;

                if (end)
                    *end = '\n';
                s2 = getHidden("\001References: ", end2);
                if (end)
                    *end = '\0';

                if (s2) {
                    final = new char[strlen(s) + strlen(s2) + 2];
                    sprintf(final, "%s %s", s2, s);
                    if (end2)
                        *end2 = '\n';
                } else
                    final = (char *) s;

                mhead.changeMsgID(final);
                if (s2)
                    delete[] final;
                if (end)
                    *end = '\n';
            }
        }

        // Get Newsgroups:
        if (!mhead.getNewsgrps()) {
            s = getHidden("\001Newsgroups: ", end);
            if (s) {
                mhead.changeNewsgrps(s);
                if (end)
                    *end = '\n';
            }
        }

        // Get Followup-To:
        if (!mhead.getFollow()) {
            s = getHidden("\001Followup-To: ", end);
            if (s) {
                mhead.changeFollow(s);
                if (end)
                    *end = '\n';
            }
        }

        // Get extended subject:
        s = getHidden("\001Subject: ", end);
        if (s) {
            mhead.changeSubject(s);
            if (end)
                *end = '\n';
        }

        // Replace the date (there's more info in the standard
        // Usenet date header; plus, it differs from the value
        // in the Blue Wave header in the packets I have):
        s = getHidden("\001Date: ", end);
        if (s) {
            mhead.changeDate(s);
            if (end)
                *end = '\n';
        }

    } else
        fidocheck(mhead);
}

void bluewave::findInfBaseName()
{
    const char *q = mm->workList->exists(".inf");
    size_t len = strlen(q) - 4;
    if (len > 8)
        len = 8;
    memcpy(packetBaseName, q, len);
    packetBaseName[len] = '\0';
}

// Read .INF file
void bluewave::initInf()
{
    FILE *infFile = openFile("INF");

    // Header

    if (!fread(&infoHeader, ORIGINAL_INF_HEADER_LEN, 1, infFile))
        fatalError("Error reading .INF file");

    infoHeaderLen = getshort(infoHeader.inf_header_len);
    if (infoHeaderLen < ORIGINAL_INF_HEADER_LEN)
        infoHeaderLen = ORIGINAL_INF_HEADER_LEN;

    infoAreainfoLen = getshort(infoHeader.inf_areainfo_len);
    if (infoAreainfoLen < ORIGINAL_INF_AREA_LEN)
        infoAreainfoLen = ORIGINAL_INF_AREA_LEN;

    from_to_len = infoHeader.from_to_len;
    if (!from_to_len || (from_to_len > 35))
        from_to_len = 35;

    subject_len = infoHeader.subject_len;
    if (!subject_len || (subject_len > 71))
        subject_len = 71;

    mixStructLen = getshort(infoHeader.mix_structlen);
    if (mixStructLen < ORIGINAL_MIX_STRUCT_LEN)
        mixStructLen = ORIGINAL_MIX_STRUCT_LEN;

    ftiStructLen = getshort(infoHeader.fti_structlen);
    if (ftiStructLen < ORIGINAL_FTI_STRUCT_LEN)
        ftiStructLen = ORIGINAL_FTI_STRUCT_LEN;

    hasOffConfig = (getshort(infoHeader.ctrl_flags) & INF_NO_CONFIG) ?
                   0 : OFFCONFIG;

    LoginName = strdupplus((char *) infoHeader.loginname);
    AliasName = strdupplus((char *) infoHeader.aliasname);
    SysOpName = strdupplus((char *) infoHeader.sysop);
    BBSName = strdupplus((char *) infoHeader.systemname);

    // Areas

    maxConf = (mm->workList->getSize() - infoHeaderLen) / infoAreainfoLen + 1;

    areas = new INF_AREA_INFO[maxConf];
    mixID = new int[maxConf];

    for (int d = 1; d < maxConf; d++) {
        fseek(infFile, (infoHeaderLen + (d - 1) * infoAreainfoLen), SEEK_SET);
        if (!fread(&areas[d], ORIGINAL_INF_AREA_LEN, 1, infFile))
            fatalError("Premature EOF in bluewave::initInf");
        mixID[d] = -1;
    }

    fclose(infFile);

    // Bulletins, etc.

    listBulletins((const char (*)[13]) infoHeader.readerfiles, 5);
}

// Read .MIX file, match .INF records to .MIX records
void bluewave::initMixID()
{
    // Read .MIX file

    FILE *mixFile = openFile("MIX");

    noOfMixRecs = (int) (mm->workList->getSize() / mixStructLen);
    mixRecord = new MIX_REC[noOfMixRecs];

    if (!fread(mixRecord, mixStructLen, noOfMixRecs, mixFile))
        fatalError("Error reading .MIX file");
    fclose(mixFile);

    // Match records

    int mixIndex = 0, c, d;
    personal = 0;

    for (c = 0; c < noOfMixRecs; c++) {
        for (d = mixIndex + 1; d < maxConf; d++)
            if (!strncasecmp((char *) areas[d].areanum,
                (char *) mixRecord[c].areanum, 6)) {

                mixID[d] = c;
                mixIndex = d;
                break;
            }
        personal += getshort(mixRecord[c].numpers);
    }

    hasPers = !(!personal);
    if (hasPers) {
        persNdx = new perstype[personal];
        int maxpers = personal;
        personal = 0;

        FTI_REC ftiRec;

        for (c = 1; c < maxConf; c++)
            if ((mixID[c] != -1) && getshort(mixRecord[mixID[c]].numpers)) {
                int totmsgs = getshort(mixRecord[mixID[c]].totmsgs);

                for (d = 0; d < totmsgs; d++) {
                    fseek(ftiFile, getlong(mixRecord[mixID[c]].msghptr) +
                          (long) d * ftiStructLen, SEEK_SET);

                    if (!fread(&ftiRec, ORIGINAL_FTI_STRUCT_LEN, 1, ftiFile))
                        fatalError("Error reading .FTI file");

                    cropesp((char *) ftiRec.to);
                    if ((!strcasecmp((char *) ftiRec.to, LoginName) ||
                         !strcasecmp((char *) ftiRec.to, AliasName)) &&
                        (personal < maxpers)) {

                        persNdx[personal].area = c;
                        persNdx[personal++].msgnum = d;
                    }
                }
            }
    } else {
        areas++;
        mixID++;
        maxConf--;
    }
}

FILE *bluewave::openFile(const char *extent)
{
    FILE *tmp;
    char fname[25];

    sprintf(fname, "%s.%s", packetBaseName, extent);

    tmp = mm->workList->ftryopen(fname);
    if (!tmp) {
        sprintf(fname, "Could not open .%s file", extent);
        fatalError(fname);
    }
    return tmp;
}

const char *bluewave::oldFlagsName()
{
    static char xtiFileN[13];

    sprintf(xtiFileN, "%s.xti", packetBaseName);
    return xtiFileN;
}

// Read in an .XTI file
bool bluewave::readOldFlags()
{
    FILE *xtiFile;

    xtiFile = mm->workList->ftryopen(oldFlagsName());
    if (!xtiFile)
        return false;

    area_list *al = mm->areaList;
    int maxareas = al->noOfAreas();
    for (int c = 0; c < maxareas; c++) {
        al->gotoArea(c);
        if (!al->isCollection()) {
            al->getLetterList();
            letter_list *ll = mm->letterList;

            for (int d = 0; d < ll->noOfLetter(); d++) {
                XTI_REC xtiRec;
                int stat;

                if (!fread(&xtiRec, sizeof xtiRec, 1, xtiFile))
                    fatalError("Error reading .XTI file");

                stat = ((xtiRec.flags & XTI_HAS_READ) ? MS_READ : 0) |
                       ((xtiRec.flags & XTI_HAS_REPLIED) ? MS_REPLIED : 0) |
                       ((xtiRec.flags & XTI_IS_PERSONAL) ? MS_PERSTO : 0) |
                       ((xtiRec.flags & XTI_HAS_SAVED) ? MS_SAVED : 0) |
                       (xtiRec.marks ? MS_MARKED : 0);

                ll->gotoLetter(d);
                ll->setStatus(stat);
            }
            delete ll;
        }
    }
    fclose(xtiFile);

    return true;
}

// Write out an .XTI file
bool bluewave::saveOldFlags()
{
    FILE *xtiFile;

    xtiFile = fopen(oldFlagsName(), "wb");
    if (!xtiFile)
        return false;

    area_list *al = mm->areaList;
    int maxareas = al->noOfAreas();
    for (int c = 0; c < maxareas; c++) {
        al->gotoArea(c);
        if (!al->isCollection()) {
            al->getLetterList();
            letter_list *ll = mm->letterList;

            for (int d = 0; d < ll->noOfLetter(); d++) {
                ll->gotoLetter(d);
                int stat = ll->getStatus();

                XTI_REC xtiRec;

                xtiRec.flags = ((stat & MS_READ) ? XTI_HAS_READ : 0) |
                               ((stat & MS_REPLIED) ? XTI_HAS_REPLIED : 0) |
                               ((stat & MS_PERSTO) ? XTI_IS_PERSONAL : 0) |
                               ((stat & MS_SAVED) ? XTI_HAS_SAVED : 0);

                xtiRec.marks = (stat & MS_MARKED) ? XTI_MARK_SAVE : 0;

                fwrite(&xtiRec, sizeof xtiRec, 1, xtiFile);
            }
            delete ll;
        }
    }
    fclose(xtiFile);

    return true;
}

const char *bluewave::getTear(int)
{
    return 0;
}

INF_HEADER &bluewave::getInfHeader()
{
    return infoHeader;
}

// -----------------------------------------------------------------
// The Blue Wave reply methods
// -----------------------------------------------------------------

bwreply::upl_bw::upl_bw(const char *name) : pktreply::upl_base(name)
{
    memset(&uplRec, 0, sizeof(uplRec));
    msgid = newsgrps = extsubj = 0;
}

bwreply::upl_bw::~upl_bw()
{
    delete[] msgid;
    delete[] newsgrps;
    delete[] extsubj;
}

bwreply::bwreply(mmail *mmA, specific_driver *baseClassA) :
    pktreply(mmA, baseClassA)
{
    uplHeader = new UPL_HEADER;
}

bwreply::~bwreply()
{
    delete uplHeader;
}

bool bwreply::getRep1(FILE *uplFile, upl_bw *l, int recnum)
{
    FILE *orgfile, *destfile;
    const char *orgname;
    int c;
    long count = 0;

    fseek(uplFile, getshort(uplHeader->upl_header_len) + recnum *
          getshort(uplHeader->upl_rec_len), SEEK_SET);
    if (fread(&(l->uplRec), sizeof(UPL_REC), 1, uplFile) != 1)
        return false;

    orgname = upWorkList->exists((char *) l->uplRec.filename);

    orgfile = fopen(orgname, "rb");
    if (orgfile) {
        destfile = fopen(l->fname, "wt");
        if (destfile) {
            while ((c = fgetc(orgfile)) != EOF) {
                if (c == '\001') {
                    c = fgetc(orgfile);
                    int x;
                    bool isRef = (c == 'R'), isNews = (c == 'N'),
                         isSubj = (c == 'S');

                    if (isRef || isNews || isSubj) {
                        for (x = 0; x < (isSubj ? 8 : 11); x++)
                            fgetc(orgfile);
                    }
                    x = 0;
                    while ((c != EOF) && (c != '\n')) {
                        c = fgetc(orgfile);
                        x++;
                    }
                    if (isRef || isNews || isSubj) {
                        char *tmp = new char[x];
                        fseek(orgfile, x * -1, SEEK_CUR);
                        if (!fread(tmp, 1, x, orgfile))
                            fatalError("Error reading reply file");
                        strtok(tmp, "\r");
                        if (isRef)
                            l->msgid = tmp;
                        else
                            if (isNews)
                                l->newsgrps = tmp;
                            else
                                l->extsubj = tmp;
                    }
                    c = '\r';
                }
                if (c != '\r') {
                    fputc(c, destfile);
                    count++;
                }
            }
            fclose(destfile);
        }
        fclose(orgfile);
    }

    l->msglen = count;

    remove(orgname);

    return true;
}

void bwreply::getReplies(FILE *uplFile)
{
    if (!fread(uplHeader, sizeof(UPL_HEADER), 1, uplFile))
        fatalError("Error reading UPL file");

    noOfLetters = (upWorkList->getSize() -
                   getshort(uplHeader->upl_header_len)) /
                  getshort(uplHeader->upl_rec_len);

    upl_bw baseUplList, *currUplList = &baseUplList;

    for (int c = 0; c < noOfLetters; c++) {
        currUplList->nextRecord = new upl_bw;
        currUplList = (upl_bw *) currUplList->nextRecord;
        if (!getRep1(uplFile, currUplList, c)) {
            delete currUplList;
            break;
        }
    }
    uplListHead = baseUplList.nextRecord;
}

area_header *bwreply::getNextArea()
{
    return new area_header(mm, 0, "REPLY", "REPLIES",
        "Letters written by you", "Blue Wave replies",
        (COLLECTION | REPLYAREA | ACTIVE | PUBLIC | PRIVATE),
        noOfLetters, 0, 35, 71);
}


int bwreply::getAreaFromTag(const char *tag)
{
    int areaNo = 0;
    for (int c = 0; c < mm->areaList->noOfAreas(); c++)
        if (!strcmp(mm->areaList->getName(c), tag)) {
            areaNo = c;
            break;
        }

    return areaNo;
}

letter_header *bwreply::getNextLetter()
{
    upl_bw *current = (upl_bw *) uplListCurrent;
    time_t unixTime = (time_t) getlong(current->uplRec.unix_date);

    char date[30];
    strftime(date, 30, "%b %d %Y  %H:%M", localtime(&unixTime));

    int areaNo = getAreaFromTag((char *) current->uplRec.echotag);

    const char *msgid;
    net_address na;
    bool isInet = (current->uplRec.network_type == INF_NET_INTERNET);
    if (isInet) {
        na.isInternet = true;
        na = (char *) current->uplRec.net_dest;
        msgid = current->msgid;
    } else {
        na.zone = getshort(current->uplRec.destzone);
        if (na.zone) {
            na.net = getshort(current->uplRec.destnet);
            na.node = getshort(current->uplRec.destnode);
            na.point = getshort(current->uplRec.destpoint);
            na.isSet = true;
        }

        msgid = (char *) current->uplRec.net_dest;
        if (!strncasecmp(msgid, "REPLY: ", 7))
            msgid += 7;
        else
            msgid = 0;
    }

    letter_header *newLetter = new letter_header(mm, current->extsubj ?
        current->extsubj : (char *) current->uplRec.subj,
        (char *) current->uplRec.to, (char *) current->uplRec.from,
        date, msgid, getlong(current->uplRec.replyto), currentLetter,
        currentLetter, areaNo,
        !(!(getshort(current->uplRec.msg_attr) & UPL_PRIVATE)),
        current->msglen, this, na, isInet, current->newsgrps);

    currentLetter++;
    uplListCurrent = uplListCurrent->nextRecord;
    return newLetter;
}

void bwreply::enterLetter(letter_header &newLetter,
                          const char *newLetterFileName, long length)
{
    upl_bw *newList = new upl_bw(newLetterFileName);

    int msg_attr = 0;

    // fill the fields of UPL_REC
    strncpy((char *) newList->uplRec.from, newLetter.getFrom(), 35);
    strncpy((char *) newList->uplRec.to, newLetter.getTo(), 35);
    strncpy((char *) newList->uplRec.subj, newLetter.getSubject(), 71);
    strcpy((char *) newList->uplRec.echotag, mm->areaList->getName());

    bool usenet = mm->areaList->isUsenet();
    bool internet = mm->areaList->isInternet();

    if (internet || usenet)
        newList->uplRec.network_type = INF_NET_INTERNET;

    const char *msgid = newLetter.getMsgID();
    if (msgid) {
        if (!(internet || usenet))
            sprintf((char *) newList->uplRec.net_dest, "REPLY: %.92s", msgid);
        else
            if (usenet)
                newList->msgid = strdupplus(msgid);
    }
    if (usenet) {
        newList->newsgrps = strdupplus(newLetter.getNewsgrps());
        if (strlen(newLetter.getSubject()) > 71)
            newList->extsubj = strdupplus(newLetter.getSubject());
    }

    putlong(newList->uplRec.unix_date, (long) time(0));

    net_address &na = newLetter.getNetAddr();
    if (na.isSet) {
        msg_attr |= UPL_NETMAIL;
        if (na.isInternet)
            sprintf((char *) newList->uplRec.net_dest,
                    "%.99s", (const char *) na);
        else {
            putshort(newList->uplRec.destzone, na.zone);
            putshort(newList->uplRec.destnet, na.net);
            putshort(newList->uplRec.destnode, na.node);
            putshort(newList->uplRec.destpoint, na.point);
        }
    }

    putlong(newList->uplRec.replyto, newLetter.getReplyTo());

    if (newLetter.getPrivate())
        msg_attr |= UPL_PRIVATE;
    if (newLetter.getReplyTo())
        msg_attr |= UPL_IS_REPLY;
    putshort(newList->uplRec.msg_attr, msg_attr);

    newList->msglen = length;

    addUpl(newList);
}

void bwreply::addRep1(FILE *uplFile, upl_base *node, int)
{
    FILE *orgfile, *destfile;
    upl_bw *l = (upl_bw *) node;
    const char *dest;
    int c;

    dest = freeFileName();
    strcpy((char *) l->uplRec.filename, dest);

    orgfile = fopen(l->fname, "rt");
    if (orgfile) {
        destfile = fopen(dest, "wb");
        if (destfile) {
            if (l->uplRec.network_type == INF_NET_INTERNET) {
                fprintf(destfile, *(l->uplRec.net_dest) ?
                        "\001X-Mail" : "\001X-News");
                fprintf(destfile, "reader: " MM_NAME
                        " Offline Reader for %s v"
                        MM_VERNUM "\r\n", sysname());

                if (l->msgid)
                    fprintf(destfile, "\001References: %s\r\n", l->msgid);
                if (l->newsgrps)
                    fprintf(destfile, "\001Newsgroups: %s\r\n", l->newsgrps);
                if (l->extsubj)
                    fprintf(destfile, "\001Subject: %s\r\n", l->extsubj);
            }

            while ((c = fgetc(orgfile)) != EOF) {
                if (c == '\n')
                    fputc('\r', destfile);
                fputc(c, destfile);
            }
            fclose(destfile);
        }
        fclose(orgfile);
    }
    fwrite(&(l->uplRec), sizeof(UPL_REC), 1, uplFile);
}

void bwreply::addHeader(FILE *uplFile)
{
    UPL_HEADER newUplHeader;
    memset(&newUplHeader, 0, sizeof(UPL_HEADER));

    putshort(newUplHeader.upl_header_len, sizeof(UPL_HEADER));
    putshort(newUplHeader.upl_rec_len, sizeof(UPL_REC));

    newUplHeader.reader_major = MM_MAJOR;
    newUplHeader.reader_minor = MM_MINOR;
    strncpy((char *) newUplHeader.vernum, MM_VERNUM, 20);
    for (int c = 0; newUplHeader.vernum[c]; newUplHeader.vernum[c++] -= 10);

    const char *name = sysname();
    size_t len = strlen(name);

    if (len < 80 - sizeof(MM_NAME))
        sprintf((char *) newUplHeader.reader_name, MM_NAME "/%s", name);
    else
        strcpy((char *) newUplHeader.reader_name, MM_NAME);

    if (len < 16 - sizeof(MM_NAME))
        strcpy((char *) newUplHeader.reader_tear,
               (char *) newUplHeader.reader_name);
    else if (len < 16 - sizeof(MM_SNAME))
        sprintf((char *) newUplHeader.reader_tear, MM_SNAME "/%s", name);
    else
        strcpy((char *) newUplHeader.reader_tear, MM_NAME);

    strcpy((char *) newUplHeader.loginname, baseClass->getLoginName());
    strcpy((char *) newUplHeader.aliasname, baseClass->getAliasName());

    fwrite(&newUplHeader, sizeof(UPL_HEADER), 1, uplFile);
}

const char *bwreply::freeFileName()
{
    mystat st;
    static char testFileName[13];

    for (long c = 0; c <= 99999; c++) {
        sprintf(testFileName, "%05ld.MSG", c);
        if (!st.init(testFileName))
            break;
    }
    return testFileName;
}

void bwreply::repFileName()
{
    int x;
    const char *basename = baseClass->getBaseName();

    for (x = 0; basename[x]; x++) {
        replyPacketName[x] = tolower(basename[x]);
        replyInnerName[x] = toupper(basename[x]);
    }
    strcpy(replyPacketName + x, ".new");
    strcpy(replyInnerName + x, ".UPL");
}

const char *bwreply::repTemplate(bool offres)
{
    static char buff[20];

    if (offres)
        sprintf(buff, "*.*");
    else
        sprintf(buff, "%s *.MSG", replyInnerName);

    return buff;
}

char *bwreply::nextLine(FILE *olc)
{
    static char line[128];

    char *end = myfgets(line, sizeof line, olc);
    if (end)
        while ((*end == '\n') || (*end == '\r'))
            *end-- = '\0';
    return line;
}

bool bwreply::getOffConfig()
{
    PDQ_REC pdqrec;
    FILE *olc;
#ifdef BOGUS_WARNING
    char *line = 0;
#else
    char *line;
#endif
    bool status, oldstyle;

    upWorkList = new file_list(mm->resourceObject->get(UpWorkDir));

    olc = upWorkList->ftryopen(".pdq");
    if (olc)
        oldstyle = true;
    else {
        olc = upWorkList->ftryopen(".olc");
        oldstyle = false;
    }

    if (olc) {
        if (oldstyle) {
            fseek(olc, sizeof(PDQ_HEADER), SEEK_SET);
            if (!fread(&pdqrec, 1, PDQ_REC_SIZE, olc))
                fatalError("Error reading .PDQ file");
        } else {
            nextLine(olc);
            do
                line = nextLine(olc);
            while (line[0] != '[');
        }

        int areaNo = -1;
        int maxareas = mm->areaList->noOfAreas();

        do {
            if (oldstyle)
                line = (char *) pdqrec.echotag;
            else {
                line++;
                line[strlen(line) - 1] = '\0';
            }
            for (int c = areaNo + 1; c < maxareas; c++) {
                mm->areaList->gotoArea(c);
                if (!strcmp(mm->areaList->getName(), line)) {
                    mm->areaList->Add();
                    areaNo = c;
                    break;
                } else
                    mm->areaList->Drop();
            }

            if (oldstyle) {
                if (!fread(&pdqrec, 1, PDQ_REC_SIZE, olc))
                    fatalError("Error reading .PDQ file");
            } else {
                nextLine(olc);
                nextLine(olc);
                line = nextLine(olc);
            }
        } while (!feof(olc));

        fclose(olc);
        upWorkList->kill();

        while (++areaNo < maxareas) {
            mm->areaList->gotoArea(areaNo);
            mm->areaList->Drop();
        }
        status = true;
    } else
        status = false;

    delete upWorkList;
    return status;
}

bool bwreply::makeOffConfig()
{
    FILE *olc;
    char fname[13];
    INF_HEADER &infoHeader = ((bluewave *) baseClass)->getInfHeader();

    bool oldstyle = (infoHeader.ver < 3);

    sprintf(fname, oldstyle ? "%s.PDQ" : "%s.OLC",
            findBaseName(replyInnerName));

    olc = fopen(fname, "wb");
    if (!olc)
        return false;

    if (oldstyle) {
        PDQ_HEADER pdqhead;
        int i;

        memcpy(pdqhead.keywords, infoHeader.keywords,
               sizeof infoHeader.keywords);
        memcpy(pdqhead.filters, infoHeader.filters, sizeof infoHeader.filters);
        for (i = 0; i < 3; i++) {
            strncpy((char *) pdqhead.macros[i],
                    (const char *) infoHeader.macros[i], 77);
            pdqhead.macros[i][77] = '\0';
        }
        memcpy(pdqhead.password, infoHeader.password,
               sizeof infoHeader.password);
        pdqhead.passtype = infoHeader.passtype;

        int oflags = getshort(infoHeader.uflags);
        oflags |= PDQ_AREA_CHANGES;
        putshort(pdqhead.flags, oflags);

        fwrite(&pdqhead, 1, sizeof pdqhead, olc);
    } else
        fprintf(olc, "[Global Mail Host Configuration]\r\n"
                     "AreaChanges = ON\r\n\r\n");

    int oldarea = mm->areaList->getAreaNo();

    int maxareas = mm->areaList->noOfAreas();
    for (int x = 0; x < maxareas; x++) {
        mm->areaList->gotoArea(x);
        unsigned long attrib = mm->areaList->getType();
        if (!(attrib & COLLECTION) && (((attrib & ACTIVE)
            && !(attrib & DROPPED)) || (attrib & ADDED))) {

            if (oldstyle) {
                PDQ_REC p;
                memset(&p, 0, sizeof p);
                strcpy((char *) (p.echotag), mm->areaList->getName());
                fwrite(&p, 1, PDQ_REC_SIZE, olc);
            } else
                fprintf(olc, "[%s]\r\nScan = %s\r\n\r\n",
                        mm->areaList->getName(), (attrib & PERSONLY) ?
                        "PERSONLY" : ((attrib & PERSALL) ? "PERSALL" : "ALL"));
        }
    }
    mm->areaList->gotoArea(oldarea);
    fclose(olc);

    return true;
}
