/*
 * MultiMail offline mail reader
 * OPX

 Copyright 1999-2019 William McBrine <wmcbrine@gmail.com>
 Distributed under the GNU General Public License, version 3 or later. */

#include "opx.h"
#include "compress.h"

// -----------------------------------------------------------------
// The OPX methods
// -----------------------------------------------------------------

opxpack::opxpack(mmail *mmA) : pktbase(mmA)
{
    readBrdinfoDat();

    infile = mm->workList->ftryopen("mail.dat");
    if (!infile)
        fatalError("Could not open MAIL.DAT");

    buildIndices();
}

opxpack::~opxpack()
{
    cleanup();
}

area_header *opxpack::getNextArea()
{
    int cMsgNum = areas[ID].nummsgs;
    bool x = (areas[ID].num == - 1);

    area_header *tmp = new area_header(mm, ID + 1,
        areas[ID].numA, areas[ID].name,
        (x ? "Letters addressed to you" : areas[ID].name),
        (x ? "OPX personal" : "OPX"),
        areas[ID].attr | (cMsgNum ? ACTIVE : 0), cMsgNum, 0, 35, 71);
    ID++;
    return tmp;
}

void opxpack::buildIndices()
{
    FILE *fdxFile = 0;    // warning suppression
    msgHead mhead;
    fdxHeader fhead;
    fdxRec frec;
    int x, totMsgs = 0;

    body = new bodytype *[maxConf];

    for (x = 0; x < maxConf; x++) {
        body[x] = 0;
        areas[x].nummsgs = 0;
    }

    ndx_fake base, *tmpndx = &base;

    long counter, length, personal = 0;
    long mdatlen = mm->workList->getSize();

    bool hasFdx = (mm->workList->exists("mail.fdx") != 0);

    if (hasFdx) {
        fdxFile = mm->workList->ftryopen("mail.fdx");
        if (fdxFile) {
            if (fread(&fhead, FDX_HEAD_SIZE, 1, fdxFile) &&
                getshort(fhead.PageCount) == 1) {

                totMsgs = getshort(fhead.RowsInPage);
                fseek(fdxFile, 4, SEEK_CUR);
                if (!fread(&frec, FDX_REC_SIZE, 1, fdxFile)) {
                    hasFdx = false;
                    fclose(fdxFile);
                }
            } else {
                // Skip it, we don't understand it
                hasFdx = false;
                fclose(fdxFile);
            }
        } else
            hasFdx = false;
    }

    numMsgs = 0;

    while ((hasFdx ? (numMsgs < totMsgs) :
           fread(&mhead, MSG_HEAD_SIZE, 1, infile))) {

        bool pers;

        if (hasFdx) {
            counter = getlong(frec.offset) + MSG_HEAD_SIZE;
            x = getXNum(getshort(frec.confnum));
            pers = ('D' == frec.msgtype);

            bool fdxAvail = (numMsgs < (totMsgs - 1));
            if (fdxAvail)
                if (!fread(&frec, FDX_REC_SIZE, 1, fdxFile))
                    fatalError("Error reading .FDX file");
            length = (fdxAvail ? getlong(frec.offset) : mdatlen) - counter;
        } else {
            counter = ftell(infile);
            x = getXNum(getshort(mhead.confnum));
            pers = ('D' == mhead.msgtype);

            length = getshort(mhead.length) - 0xbe;

            fseek(infile, length, SEEK_CUR);
        }

        tmpndx->next = new ndx_fake;
        tmpndx = tmpndx->next;

        tmpndx->confnum = x;

        tmpndx->pers = pers;
        if (pers)
            personal++;

        tmpndx->pointer = counter;
        tmpndx->length = length;

        numMsgs++;
        areas[x].nummsgs++;
    }

    if (hasFdx)
        fclose(fdxFile);

    initBody(base.next, personal);
}

letter_header *opxpack::getNextLetter()
{
    msgHead mhead;
    unsigned long pos, rawpos;
    int areaID, letterID;

    rawpos = body[currentArea][currentLetter].pointer;
    pos = rawpos - MSG_HEAD_SIZE;

    fseek(infile, pos, SEEK_SET);
    if (!fread(&mhead, MSG_HEAD_SIZE, 1, infile))
        fatalError("Error reading MAIL.DAT");

    if (areas[currentArea].num == -1) {
        areaID = getXNum(getshort(mhead.confnum));
        letterID = getYNum(areaID, rawpos);
    } else {
        areaID = currentArea;
        letterID = currentLetter;
    }

    net_address na;
    if (areas[areaID].attr & INTERNET)
        na = mhead.f.from;
    else {
        na.zone = getshort(mhead.f.orig_zone);
        if (na.zone) {
            na.net = getshort(mhead.f.orig_net);
            na.node = getshort(mhead.f.orig_node);
            na.point = 0;   // set from getBody()
            na.isSet = true;
        }
    }

    bool privat = getshort(mhead.f.attr) & OPX_PRIVATE;

    char date[30];
    strftime(date, 30, "%b %d %Y  %H:%M",
             getdostime(getlong(mhead.f.date_written)));

    currentLetter++;

    return new letter_header(mm, mhead.f.subject, mhead.f.to,
        mhead.f.from, date, 0, getshort(mhead.f.reply), letterID,
        getshort(mhead.msgnum), areaID, privat,
        getshort(mhead.length), this, na,
        !(!(areas[areaID].attr & LATINCHAR)));
}

void opxpack::getblk(int, long &offset, long blklen,
                     unsigned char *&p, unsigned char *&begin)
{
    int lastkar = -1;

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

        if (!kar)
            kar = ' ';

        // "Soft CRs" are misused in some OPX packets:
        if (kar == 0x8d)
            kar = '\n';

        // Line endings are a mess in OPX -- can be
        // CR, LF, _or_ CR/LF!

        if ((lastkar == '\r') && (kar != '\n')) {
            *p++ = '\n';
            begin = p;
            offset = ftell(infile) - 1;
        }

        if (kar != '\r')
            *p++ = kar;

        if (kar == '\n') {
            begin = p;
            offset = ftell(infile);
        }

        lastkar = kar;
    }
}

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

    char *end;

    const char *s = getHidden("\001INETORIG ", end);
    if (s) {
        net_address &na = mhead.getNetAddr();

        na = s;
        if (end)
            *end = '\n';
    } else
        fidocheck(mhead);
}

// Read a Borland Pascal pstring, return a C string
char *opxpack::pstrget(void *src)
{
    unsigned len = (unsigned) *((unsigned char *) src);
    char *dest = new char[len + 1];

    strncpy(dest, ((const char *) src) + 1, len);
    dest[len] = '\0';

    return dest;
}

void opxpack::readBrdinfoDat()
{
    FILE *brdinfoFile, *ocfgFile;
    brdHeader header;
    brdRec boardrec;
    ocfgRec offrec;
    char *p, *q, tmp[80];
    int brdCount, extCount;
    bool hasExtra;

    ocfgFile = mm->workList->ftryopen("dusrcfg.dat");
    if (ocfgFile) {
        if (fread(&confhead, OCFG_HEAD_SIZE, 1, ocfgFile))
            hasOffConfig = OFFCONFIG;
        else {
            hasOffConfig = 0;
            fclose(ocfgFile);
        }
    } else
        hasOffConfig = 0;

    brdinfoFile = mm->workList->ftryopen("brdinfo.dat");
    if (!brdinfoFile)
        fatalError("Could not open BRDINFO.DAT");

    if (!fread(&header, BRD_HEAD_SIZE, 1, brdinfoFile))
        fatalError("Error reading BRDINFO.DAT");

    p = pstrget(&header.bbsid);
    strcpy(packetBaseName, p);
    delete[] p;

    BBSName = pstrget(&header.bbsname);
    SysOpName = pstrget(&header.sysopname);
    BBSProg = pstrget(&header.bbstype);

    p = pstrget(&header.doorid);
    q = pstrget(&header.doorver);
    sprintf(tmp, "%s %s", p, q);
    DoorProg = strdupplus(tmp);
    delete[] q;
    delete[] p;

    LoginName = pstrget(&header.username);
    AliasName = strdupplus(LoginName);

    bulletins = header.readerfiles ? new char[header.readerfiles * 13] : 0;

    int c;
    for (c = 0; c < header.readerfiles; c++) {
        pstring(readerf,12);

        if (!fread(&readerf, 13, 1, brdinfoFile))
            fatalError("Error reading BRDINFO.DAT");
        strncpy(bulletins + c * 13, (char *) readerf + 1, *readerf);
        bulletins[c * 13 + *readerf] = '\0';
    }

    if (header.readerfiles > 1) {
        listBulletins((const char (*)[13]) (bulletins + 13),
                      header.readerfiles - 1, 1);
        hello = strdupplus(bulletins);
    } else
        listBulletins(0, 0, 1);

    // Skip old numofareas byte:
    fseek(brdinfoFile, 1, SEEK_CUR);

    maxConf = getshort(header.numofareas) + 1;
    areas = new AREAs[maxConf];

    hasExtra = (mm->workList->exists("extareas.dat") != 0);
    extCount = hasExtra ? (mm->workList->getSize() / BRD_REC_SIZE) : 0;
    brdCount = maxConf - extCount;

    areas[0].num = -1;
    strcpy(areas[0].numA, "PERS");
    areas[0].name = strdupplus("PERSONAL");
    areas[0].attr = PUBLIC | PRIVATE | COLLECTION;

    for (c = 1; c < maxConf; c++) {
        if (hasExtra && (c == brdCount)) {
            fclose(brdinfoFile);
            brdinfoFile = mm->workList->ftryopen("extareas.dat");
            if (!brdinfoFile)
                fatalError("Could not open EXTAREAS.DAT");
        }
        if (!fread(&boardrec, BRD_REC_SIZE, 1, brdinfoFile))
            fatalError("Error reading BRDINFO.DAT");

        areas[c].num = getshort(boardrec.confnum);
        sprintf(areas[c].numA, "%d", areas[c].num);

        areas[c].name = pstrget(&boardrec.name);

        bool selected = !(!boardrec.scanned);
        if (hasOffConfig) {
            if (fread(&offrec, OCFG_REC_SIZE, 1, ocfgFile) &&
                getshort(offrec.confnum) == (unsigned) areas[c].num)

                selected = !(!offrec.scanned);
        }

        unsigned rawattr = getshort(boardrec.attrib);
        areas[c].attr = (((rawattr & OPX_NETMAIL) |
                        (boardrec.attrib2 & OPX_INTERNET)) ? NETMAIL : 0) |
                        ((rawattr & OPX_PRIVONLY) ? 0 : PUBLIC) |
                        ((rawattr & OPX_PUBONLY) ? 0 : PRIVATE) |
                        (((boardrec.attrib2 & OPX_USENET) |
                        (boardrec.attrib2 & OPX_INTERNET)) ? (INTERNET |
                        LATINCHAR) : 0) | (selected ? ACTIVE : 0) |
                        hasOffConfig | (hasOffConfig ? SUBKNOWN : 0);
    }

    fclose(brdinfoFile);

    if (hasOffConfig)
        fclose(ocfgFile);
}

ocfgHeader *opxpack::offhead()
{
    return &confhead;
}

const char *opxpack::oldFlagsName()
{
    return "mail.fdx";
}

// Read in an .FDX file
bool opxpack::readOldFlags()
{
    FILE *fdxFile;
    fdxHeader fhead;
    fdxRec frec;
    int totmsgs, area = -1, lastarea, msgnum;
    letter_list *ll = 0;

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

    if (!fread(&fhead, FDX_HEAD_SIZE, 1, fdxFile) ||
        getshort(fhead.PageCount) != 1) {

        fclose(fdxFile);
        return false;
    }

    fseek(fdxFile, 4, SEEK_CUR);
    totmsgs = getshort(fhead.RowsInPage);

    area_list *al = mm->areaList;

    for (int x = 0; x < totmsgs; x++) {
        if (!fread(&frec, FDX_REC_SIZE, 1, fdxFile))
            fatalError("Error reading .FDX file");

        lastarea = area;
        area = getshort(frec.confnum);
        msgnum = getshort(frec.msgnum);

        if (lastarea != area) {
            delete ll;
            al->gotoArea(getXNum(area) + 1);
            al->getLetterList();
            ll = mm->letterList;
            ll->gotoLetter(-1);
        }

        ll->findMsgNum(msgnum);

        int stat = ((frec.flags & FDX_READ) ? MS_READ : 0) |
                   ((frec.flags & FDX_REPLIED) ? MS_REPLIED : 0) |
                   ((frec.marks & FDX_TAGGED) ? MS_MARKED : 0) |
                   ((frec.msgtype == 'D') ? MS_PERSTO : 0);

        ll->setStatus(stat);
    }
    delete ll;

    fclose(fdxFile);

    return true;
}

// Write out an .FDX file
bool opxpack::saveOldFlags()
{
    FILE *fdxFile;

    // If there are more messages than will fit in a 64K block,
    // it would need multiple pages, so forget it:

    if (((long) numMsgs * FDX_REC_SIZE) > 0x10000)
        return false;

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

    // Fill the header and write it out:

    fdxHeader fhead;
    putshort(fhead.RowsInPage, numMsgs);
    putshort(fhead.ColsInPage, 1);
    putshort(fhead.PagesDown, 1);
    putshort(fhead.PagesAcross, 1);
    putshort(fhead.ElSize, FDX_REC_SIZE);
    putshort(fhead.PageSize, numMsgs * FDX_REC_SIZE);
    putshort(fhead.PageCount, 1);
    putlong(fhead.NextAvail, (long) numMsgs * FDX_REC_SIZE +
            FDX_HEAD_SIZE + 4);
    memcpy(fhead.ID, "\006VARRAY", 7);

    fwrite(&fhead, FDX_HEAD_SIZE, 1, fdxFile);

    plong addr;
    putlong(addr, FDX_HEAD_SIZE + 4);

    fwrite(addr, 4, 1, fdxFile);

    // And now, the body:

    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();

                fdxRec frec;

                int anum = atoi(al->getShortName());
                putshort(frec.confnum, anum);
                putshort(frec.msgnum, ll->getMsgNum());

                long offset = body[c - 1][d].pointer - MSG_HEAD_SIZE;
                putlong(frec.offset, offset);

                frec.flags = ((stat & MS_READ) ? FDX_READ : 0) |
                             ((stat & MS_REPLIED) ? FDX_REPLIED : 0);
                frec.marks = (stat & MS_MARKED) ? FDX_TAGGED : 0;
                frec.msgtype = ((stat & MS_PERSTO) ? 'D' : ' ');

                fwrite(&frec, FDX_REC_SIZE, 1, fdxFile);
            }
            delete ll;
        }
    }
    fclose(fdxFile);

    return true;
}

// -----------------------------------------------------------------
// The OPX reply methods
// -----------------------------------------------------------------

opxreply::upl_opx::upl_opx(const char *name) : pktreply::upl_base(name)
{
    memset(&rhead, 0, sizeof(rhead));
    msgid = 0;
}

opxreply::upl_opx::~upl_opx()
{
    delete[] msgid;
}

opxreply::opxreply(mmail *mmA, specific_driver *baseClassA) :
    pktreply(mmA, baseClassA)
{
}

opxreply::~opxreply()
{
}

int opxreply::getArea(const char *fname)
{
    int sum = 0;

    fname = strchr(fname, '.') + 1;
    for (int x = 0; x < 3; x++) {
        sum *= 36;

        unsigned char c = toupper(fname[x]);
        if ((c >= '0') && (c <= '9'))
            sum += c - '0';
        else
            if ((c >= 'A') && (c <= 'Z'))
                sum += c - 'A' + 10;
    }
    return sum;
}

bool opxreply::getRep1(const char *orgname, upl_opx *l)
{
    FILE *orgfile, *destfile;
    int c;
    long count = 0;

    orgfile = fopen(orgname, "rb");
    if (orgfile) {

        if (!fread(&l->rhead, FIDO_HEAD_SIZE, 1, orgfile))
            fatalError("Error reading reply file");
        l->area = getArea(orgname);

        net_address na;
        na.zone = getshort(l->rhead.dest_zone);
        if (na.zone) {
            na.net = getshort(l->rhead.dest_net);
            na.node = getshort(l->rhead.dest_node);
            na.point = 0;
           na.isSet = true;
        }

        destfile = fopen(l->fname, "wt");
        if (destfile) {
            while ((c = fgetc(orgfile)) != EOF) {
                if (c == '\001') {
                    c = fgetc(orgfile);
                    int x;
                    bool isReply = (c == 'R'), isPoint = (c == 'T'),
                         isInet = (c == 'I');

                    if (isReply || isPoint || isInet) {
                        for (x = 0; x < (isInet ? 8 : (isPoint ? 4 : 6)); x++)
                            fgetc(orgfile);
                    }
                    x = 0;
                    while ((c != EOF) && (c != '\n')) {
                        c = fgetc(orgfile);
                        x++;
                    }
                    if (isReply || isPoint || isInet) {
                        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 (isReply)
                            l->msgid = tmp;
                        else {
                            if (isInet)
                                na = tmp;
                            else
                                sscanf(tmp, "%u", &na.point);
                            delete[] tmp;
                        }
                    }
                    c = '\r';
                }
                if (c && (c != '\r')) {
                    fputc(c, destfile);
                    count++;
                }
            }
            fclose(destfile);
        }

        l->na = na;

        fclose(orgfile);
    }

    l->msglen = count;

    remove(orgname);

    return true;
}

void opxreply::getReplies(FILE *)
{
    noOfLetters = 0;

    upl_opx baseUplList, *currUplList = &baseUplList;
    const char *p;

    upWorkList->gotoFile(-1);
    while ((p = upWorkList->getNext("!")) != 0) {
        currUplList->nextRecord = new upl_opx;
        currUplList = (upl_opx *) currUplList->nextRecord;
        if (!getRep1(p, currUplList)) {
            delete currUplList;
            break;
        }
        noOfLetters++;
    }
    uplListHead = baseUplList.nextRecord;
}

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

letter_header *opxreply::getNextLetter()
{
    upl_opx *current = (upl_opx *) uplListCurrent;

    char date[30];
    strftime(date, 30, "%b %d %Y  %H:%M",
             getdostime(getlong(current->rhead.date_written)));

    int area = ((opxpack *) baseClass)->getXNum(current->area) + 1;

    letter_header *newLetter = new letter_header(mm,
        current->rhead.subject, current->rhead.to,
        current->rhead.from, date, current->msgid,
        getshort(current->rhead.reply), currentLetter, currentLetter,
        area, getshort(current->rhead.attr) & OPX_PRIVATE,
        current->msglen, this, current->na, mm->areaList->isLatin(area));

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

void opxreply::enterLetter(letter_header &newLetter,
                           const char *newLetterFileName, long length)
{
    // Specify the format separately from strftime() to suppress
    // GGC's Y2K warning:
    const char *datefmt_opx = "%d %b %y  %H:%M:%S";

    upl_opx *newList = new upl_opx(newLetterFileName);

    int attrib = newLetter.getPrivate() ? OPX_PRIVATE : 0;

    strncpy(newList->rhead.subject, newLetter.getSubject(), 71);
    strncpy(newList->rhead.from, newLetter.getFrom(), 35);
    strncpy(newList->rhead.to, newLetter.getTo(), 35);

    const char *msgid = newLetter.getMsgID();
    if (msgid)
        newList->msgid = strdupplus(msgid);

    newList->area = atoi(mm->areaList->getShortName());
    newList->na = newLetter.getNetAddr();

    putshort(newList->rhead.attr, attrib);
    putshort(newList->rhead.reply, newLetter.getReplyTo());

    time_t now = time(0);
    strftime(newList->rhead.date, 20, datefmt_opx, localtime(&now));
    unsigned long dostime = mkdostime(localtime(&now));
    putlong(newList->rhead.date_written, dostime);
    putlong(newList->rhead.date_arrived, dostime);

    newList->msglen = length;

    addUpl(newList);
}

const char *opxreply::freeFileName(upl_opx *l)
{
    static char fname[13];
    char ext[4];
    unsigned area[3], x;

    area[2] = l->area;
    area[0] = area[2] / (36 * 36);
    area[2] %= (36 * 36);
    area[1] = area[2] / 36;
    area[2] %= 36;

    for (x = 0; x < 3; x++)
        ext[x] = area[x] + ((area[x] < 10) ? '0' : ('A' - 10));
    ext[3] = '\0';

    mystat st;
    int reply = getshort(l->rhead.reply);
    if (reply) {
        sprintf(fname, "!R%d.%s", reply, ext);
        if (!st.init(fname))
            return fname;
    }
    x = 1;
    do
        sprintf(fname, "!N%d.%s", x++, ext);
    while (st.init(fname));

    return fname;
}

void opxreply::addRep1(FILE *, upl_base *node, int)
{
    FILE *orgfile, *destfile;
    upl_opx *l = (upl_opx *) node;
    const char *dest;

    dest = freeFileName(l);

    orgfile = fopen(l->fname, "rt");
    if (orgfile) {

        destfile = fopen(dest, "wb");
        if (destfile) {

            fwrite(&l->rhead, FIDO_HEAD_SIZE, 1, destfile);

            bool skipPID = false;

            if (l->na.isSet) {
                if (l->na.isInternet) {
                    fprintf(destfile, "\001INETDEST %s\r\n",
                            (const char *) l->na);
                    skipPID = true;
                } else {
                    putshort(l->rhead.dest_zone, l->na.zone);
                    putshort(l->rhead.dest_net, l->na.net);
                    putshort(l->rhead.dest_node, l->na.node);

                    // I should probably add the originating
                    // address here, but it's frequently
                    // bogus. Let's try it this way.

                    if (l->na.point)
                        fprintf(destfile, "\001TOPT %d\r\n", l->na.point);
                }
            }

            if (l->msgid)
                fprintf(destfile, "\001REPLY: %s\r\n", l->msgid);

            if (!skipPID)
                fprintf(destfile, "\001PID: " MM_NAME
                        "/%s v" MM_VERNUM "\r\n", sysname());

            int c, count = 0, lastsp = 0;
            while ((c = fgetc(orgfile)) != EOF) {
                count++;
                if ((count > 80) && lastsp) {
                    fseek(orgfile, lastsp - count, SEEK_CUR);
                    fseek(destfile, lastsp - count, SEEK_CUR);
                    c = '\n';
                }
                if ('\n' == c) {
                    fprintf(destfile, "\r\n");
                    count = lastsp = 0;
                } else {
                    fputc(c, destfile);
                    if (' ' == c)
                        lastsp = count;
                }
            }

            fprintf(destfile, "\r\n");
            fputc(0, destfile);

            fclose(destfile);
        }
        fclose(orgfile);
    }
}

void opxreply::addHeader(FILE *repFile)
{
    // <BBSID>.ID -- fake it.

    long magic = -598939720L;
    time_t now = time(0);

    fprintf(repFile, "FALSE\r\n\r\n4.4\r\nTRUE\r\n%ld\r\n0\r\n%ld\r\n",
            (signed long) mkdostime(localtime(&now)), magic);
}

void opxreply::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, ".rep");
    strcpy(replyInnerName + x, ".ID");
}

const char *opxreply::repTemplate(bool)
{
    return "*.*";
}

bool opxreply::getOffConfig()
{
    FILE *olc;
    bool status = false;

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

    olc = upWorkList->ftryopen("rusrcfg.dat");
    if (olc) {
        ocfgHeader offhead;
        ocfgRec offrec;
        int areaOPX, areaNo;

        if (!fread(&offhead, OCFG_HEAD_SIZE, 1, olc))
            fatalError("Error reading RUSRCFG.DAT");
        int totareas = getshort(offhead.numofareas);

        for (int i = 0; i < totareas; i++) {
            if (!fread(&offrec, OCFG_REC_SIZE, 1, olc))
                fatalError("Error reading RUSRCFG.DAT");
            areaOPX = getshort(offrec.confnum);

            areaNo = ((opxpack *) baseClass)->getXNum(areaOPX) + 1;
            mm->areaList->gotoArea(areaNo);

            if (offrec.scanned)
                mm->areaList->Add();
            else
                mm->areaList->Drop();
        }
        fclose(olc);
        upWorkList->kill();

        status = true;
    }
    delete upWorkList;

    return status;
}

bool opxreply::makeOffConfig()
{
    FILE *olc;
    ocfgRec offrec;

    olc = fopen("RUSRCFG.DAT", "wb");
    if (!olc)
        return false;

    fwrite(((opxpack *) baseClass)->offhead(), OCFG_HEAD_SIZE, 1, olc);

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

    int maxareas = mm->areaList->noOfAreas();
    for (int areaNo = 0; areaNo < maxareas; areaNo++) {
        mm->areaList->gotoArea(areaNo);

        unsigned long attrib = mm->areaList->getType();
        int anum = atoi(mm->areaList->getShortName());

        if (!(attrib & COLLECTION)) {
            putshort(offrec.confnum, anum);
            offrec.scanned = ((((attrib & ACTIVE) && !(attrib & DROPPED))
                             || (attrib & ADDED)));
            fwrite(&offrec, OCFG_REC_SIZE, 1, olc);
        }
    }
    mm->areaList->gotoArea(oldarea);
    fclose(olc);

    return true;
}
