/*****************************************************************************
 * AreaFix for HPT (FTN NetMail/EchoMail Tosser)
 *****************************************************************************
 * Copyright (C) 1998-2002
 *
 * Max Levenkov
 *
 * Fido:     2:5000/117
 * Internet: sackett@mail.ru
 * Novosibirsk, West Siberia, Russia
 *
 * Big thanks to:
 *
 * Fedor Lizunkov
 *
 * Fido:     2:5020/960
 * Moscow, Russia
 *
 * This file is part of HPT.
 *
 * HPT is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * HPT is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with HPT; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************
 * $Id: areafix.c,v 1.316.2.7 2003/04/27 00:14:46 d_sergienko Exp $
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#if !defined(__TURBOC__) && !(defined(_MSC_VER) && (_MSC_VER >= 1200))
#include <unistd.h>
#endif

#include <fidoconf/fidoconf.h>
#include <fidoconf/common.h>
#include <fidoconf/xstr.h>
#include <fidoconf/areatree.h>
#include <fidoconf/afixcmd.h>
#include <fidoconf/arealist.h>

#include <fcommon.h>
#include <global.h>
#include <pkt.h>
#include <version.h>
#include <toss.h>
#include <smapi/patmat.h>
#include <ctype.h>
#include <smapi/progprot.h>
#include <seenby.h>
#include <scan.h>
#include <fidoconf/recode.h>
#include <areafix.h>
#include <scanarea.h>
#include <hpt.h>
#include <dupe.h>
#include <query.h>

unsigned char RetFix;
static int rescanMode = 0;
static int rulesCount = 0;
static char **rulesList = NULL;

char *print_ch(int len, char ch)
{
    static char tmp[256];

    if (len <= 0 || len > 255) return "";
    
    memset(tmp, ch, len);
    tmp[len]=0;
    return tmp;
}

int isarcmail (char *name)
{
    char *ext = name + strlen(name) - 4;

    if (strncasecmp(ext, ".mo", 3) == 0 || strncasecmp(ext, ".tu", 3) == 0||
        strncasecmp(ext, ".we", 3) == 0 || strncasecmp(ext, ".th", 3) == 0||
        strncasecmp(ext, ".fr", 3) == 0 || strncasecmp(ext, ".sa", 3) == 0||
        strncasecmp(ext, ".su", 3) == 0) return 1;
    else return 0;  
}

/* test area-link pair to mandatory
 */
int mandatoryCheck(s_area area, s_link *link) {
    int i;

    w_log(LL_FUNC,"areafix.c::mandatoryCheck()");

    if (grpInArray(area.group,link->optGrp,link->numOptGrp)&&link->mandatory) return 1;
    if (link->numOptGrp==0 && link->mandatory) return 1;
    if (area.mandatory) return 1;
    if ((i=isAreaLink(link->hisAka, &area))!=-1) return area.downlinks[i]->mandatory;
    return 0;
}

int subscribeCheck(s_area area, s_link *link)
{
    int found = 0;

    if (isLinkOfArea(link, &area)) return 0;

    if (area.group) {
	if (config->numPublicGroup)
	    found = grpInArray(area.group,config->PublicGroup,config->numPublicGroup);
	if (!found && link->numAccessGrp)
	    found = grpInArray(area.group,link->AccessGrp,link->numAccessGrp);
    } else found = 1;

    if (!found) return 2;
    if (area.levelwrite > link->level && area.levelread > link->level) return 2;
    return 1;
}

int subscribeAreaCheck(s_area *area, char *areaname, s_link *link)
{
    int rc=4;

    if (!areaname) return rc;
    if (patimat(area->areaName,areaname)==1) {
	rc=subscribeCheck(*area, link);
	// 0 - already subscribed / linked
	// 1 - need subscribe / not linked
	// 2 - no access
    }
    // this is another area
    return rc;
}

void addlink(s_link *link, s_area *area)
{
    s_arealink *arealink;
    
    area->downlinks = safe_realloc (area->downlinks, sizeof(s_arealink*)*(area->downlinkCount+1));
    arealink = area->downlinks[area->downlinkCount] = (s_arealink*) safe_malloc(sizeof(s_arealink));

    memset(arealink, '\0', sizeof(s_arealink));
    arealink->link = link;

    if (link->numOptGrp > 0) {
	// default set export on, import on, mandatory off
	arealink->export = 1;
	arealink->import = 1;
	arealink->mandatory = 0;

	if (grpInArray(area->group,link->optGrp,link->numOptGrp)) {
	    arealink->export = link->export;
	    arealink->import = link->import;
	    arealink->mandatory = link->mandatory;
	}
    } else {
	arealink->export = link->export;
	arealink->import = link->import;
	arealink->mandatory = link->mandatory;
    }
    if (area->mandatory) arealink->mandatory = 1;
    if (link->level < area->levelread)	arealink->export=0;
    if (link->level < area->levelwrite) arealink->import=0;
    // paused link can't receive mail
    if ((link->Pause & EPAUSE) == EPAUSE) arealink->export = 0;

    area->downlinkCount++;
}

void removelink(s_link *link, s_area *area)
{
    int i;

    if ( (i=isAreaLink(link->hisAka, area)) != -1) {
	nfree(area->downlinks[i]);
	area->downlinks[i] = area->downlinks[area->downlinkCount-1];
	area->downlinkCount--;
    }
}
/* moved to fidoconfig
s_message *makeMessage (s_addr *origAddr, s_addr *destAddr,
			char *fromName,	char *toName, char *subject, int netmail)
{
    // netmail == 0 - echomail
    // netmail == 1 - netmail
    time_t time_cur;
    s_message *msg;

    if (toName == NULL) toName = "sysop";
    
    time_cur = time(NULL);
    
    msg = (s_message*) safe_malloc(sizeof(s_message));
    memset(msg, '\0', sizeof(s_message));
    
    msg->origAddr.zone = origAddr->zone;
    msg->origAddr.net = origAddr->net;
    msg->origAddr.node = origAddr->node;
    msg->origAddr.point = origAddr->point;

    msg->destAddr.zone = destAddr->zone;
    msg->destAddr.net = destAddr->net;
    msg->destAddr.node = destAddr->node;
    msg->destAddr.point = destAddr->point;
	
    xstrcat(&(msg->fromUserName), fromName);
    xstrcat(&(msg->toUserName), toName);
    xstrcat(&(msg->subjectLine), subject);

    msg->attributes |= MSGLOCAL;
    if (netmail) {
	msg->attributes |= MSGPRIVATE;
	msg->netMail = 1;
    }
    if (config->areafixKillReports) msg->attributes |= MSGKILL;

    fts_time((char*)msg->datetime, localtime(&time_cur));

    return msg;
}
*/
char *getPatternFromLine(char *line, int *reversed)
{

    *reversed = 0;
    if (!line) return NULL;
    /* original string is like "%list ! *.citycat.*" or withut '!' sign*/
    if (line[0] == '%') line++; /* exclude '%' sign */
    while((strlen(line) > 0) && isspace(line[0])) line++; /* exclude spaces between '%' and command */
    while((strlen(line) > 0) && !isspace(line[0])) line++; /* exclude command */
    while((strlen(line) > 0) && isspace(line[0])) line++; /* exclude spaces between command and pattern */

    if ((strlen(line) > 2) && (line[0] == '!') && (isspace(line[1])))
    {
        *reversed = 1;
        line++;     /* exclude '!' sign */
        while(isspace(line[0])) line++; /* exclude spaces between '!' and pattern */
    }

    if (strlen(line) > 0)
        return line;
    else
        return NULL;
}

char *list(s_link *link, char *cmdline) {
    unsigned int i, active, avail, rc = 0;
    char *report = NULL;
    char *list = NULL;
    char *pattern = NULL;
    int reversed;
    ps_arealist al;
    s_area area;

    pattern = getPatternFromLine(cmdline, &reversed);
    if ((pattern) && (strlen(pattern)>60 || !isValidConference(pattern))) {
        w_log(LL_FUNC, "areafix::list() FAILED (error request line)");
        return errorRQ(cmdline);
    }


    xscatprintf(&report, "Available areas for %s\r\r", aka2str(link->hisAka));

    al = newAreaList();
    for (i=active=avail=0; i< config->echoAreaCount; i++) {
		
	area = config->echoAreas[i];
	rc = subscribeCheck(area, link);

        if (rc < 2 && (!area.hide || (area.hide && rc==0))) { // add line
            if (pattern)
            {
                /* if matches pattern and not reversed (or vise versa) */
                if (patimat(area.areaName, pattern)!=reversed) 
                {
                    addAreaListItem(al,rc==0,area.areaName,area.description);
                    if (rc==0) active++; avail++;
                }
            } else
            {
                addAreaListItem(al,rc==0,area.areaName,area.description);
                if (rc==0) active++; avail++;
            }
	} /* end add line */

    } /* end for */
    sortAreaList(al);
    list = formatAreaList(al,78," *");
    if (list) xstrcat(&report,list);
    nfree(list);
    freeAreaList(al);

    xscatprintf(&report, "\r'*' = area active for %s\r%i areas available, %i areas active\r", aka2str(link->hisAka), avail, active);
    if (link->afixEchoLimit) xscatprintf(&report, "\rYour limit is %u areas for subscribe\r", link->afixEchoLimit);

    w_log(LL_AREAFIX, "areafix: list sent to %s", aka2str(link->hisAka));

    return report;
}

char *linked(s_link *link) {
    unsigned int i, n, rc;
    char *report = NULL;

    xscatprintf(&report, "\r%s areas on %s\r\r", 
		((link->Pause & EPAUSE) == EPAUSE) ? "Passive" : "Active", aka2str(link->hisAka));
							
    for (i=n=0; i<config->echoAreaCount; i++) {
	rc=subscribeCheck(config->echoAreas[i], link);
	if (rc==0) {
	    xscatprintf(&report, " %s\r", config->echoAreas[i].areaName);
	    n++;
	}
    }
    xscatprintf(&report, "\r%u areas linked\r", n);
    if (link->afixEchoLimit) xscatprintf(&report, "\rYour limit is %u areas for subscribe\r", link->afixEchoLimit);
    w_log(LL_AREAFIX, "areafix: linked areas list sent to %s", aka2str(link->hisAka));
    return report;
}

char *unlinked(s_link *link) {
    unsigned int i, rc;
    char *report = NULL;
    s_area *areas;

    areas=config->echoAreas;
    xscatprintf(&report, "Unlinked areas to %s\r\r", aka2str(link->hisAka));

    for (i=0; i<config->echoAreaCount; i++) {
	rc=subscribeCheck(areas[i], link);
	if (rc == 1 && !areas[i].hide) {
	    xscatprintf(&report, " %s\r", areas[i].areaName);
	}
    }
    w_log(LL_AREAFIX, "areafix: unlinked areas list sent to %s", aka2str(link->hisAka));

    return report;
}

char *help(s_link *link) {
    FILE *f;
    int i=1;
    char *help;
    long endpos;

    if (config->areafixhelp!=NULL) {
	if ((f=fopen(config->areafixhelp,"r")) == NULL) {
	    w_log (LL_ERR, "areafix: cannot open help file \"%s\": %s",
	           config->areafixhelp, strerror(errno));
	    if (!quiet)
		fprintf(stderr,"areafix: cannot open help file \"%s\": %s\n", 
		        config->areafixhelp, strerror(errno));
	    return NULL;
	}
		
	fseek(f,0L,SEEK_END);
	endpos=ftell(f);

	help=(char*) safe_malloc((size_t) endpos+1);

	fseek(f,0L,SEEK_SET);
	endpos = fread(help,1,(size_t) endpos,f);

	for (i=0; i<endpos; i++) if (help[i]=='\n') help[i]='\r';
	help[endpos]='\0';

	fclose(f);

	w_log(LL_AREAFIX, "areafix: help sent to %s",link->name);

	return help;
    }
    return NULL;
}

int tag_mask(char *tag, char **mask, unsigned num) {
    unsigned int i;

    for (i = 0; i < num; i++) {
	if (patimat(tag,mask[i])) return 1;
    }

    return 0;
}

char *available(s_link *link, char *cmdline)
{
    FILE *f;
    unsigned int j=0, found;
    unsigned int k, rc;
    char *report = NULL, *line, *token, *running, linkAka[SIZE_aka2str];
    char *pattern;
    int reversed;
    s_link *uplink=NULL;
    ps_arealist al;

    pattern = getPatternFromLine(cmdline, &reversed);
    if ((pattern) && (strlen(pattern)>60 || !isValidConference(pattern))) {
        w_log(LL_FUNC, "areafix::avail() FAILED (error request line)");
        return errorRQ(cmdline);
    }

    for (j = 0; j < config->linkCount; j++) {
	uplink = &(config->links[j]);

	found = 0;
	for (k = 0; k < link->numAccessGrp && uplink->LinkGrp; k++)
	    if (strcmp(link->AccessGrp[k], uplink->LinkGrp) == 0)
		found = 1;

	if ((uplink->forwardRequests && uplink->forwardRequestFile) &&
	    ((uplink->LinkGrp == NULL) || (found != 0))) {
	    if ((f=fopen(uplink->forwardRequestFile,"r")) == NULL) {
		w_log(LL_ERR, "areafix: cannot open forwardRequestFile \"%s\": %s",
		      uplink->forwardRequestFile, strerror(errno));
		return report;
	    }

	    xscatprintf(&report, "Available Area List from %s:\r",
			aka2str(uplink->hisAka));

	    al = newAreaList();
	    while ((line = readLine(f)) != NULL) {
		line = trimLine(line);
		if (line[0] != '\0') {
		    running = line;
		    token = strseparate(&running, " \t\r\n");
		    rc = 0;

		    if (uplink->numDfMask)
			rc |= tag_mask(token, uplink->dfMask, uplink->numDfMask);

		    if (uplink->denyFwdFile)
			rc |= IsAreaAvailable(token,uplink->denyFwdFile,NULL,0);

                    if (pattern)
                    {
                        /* if matches pattern and not reversed (or vise versa) */
                        if ((rc==0) &&(patimat(token, pattern)!=reversed))
                            addAreaListItem(al,0,token,running);
                    } else
                    {
                        if (rc==0) addAreaListItem(al,0,token,running);
                    }

		}
		nfree(line);
	    }
	    fclose(f);

	    if(al->count) {
		sortAreaList(al);
		line = formatAreaList(al,78,NULL);
		xstrcat(&report,"\r");
		xstrcat(&report,line);
		nfree(line);
	    }

	    freeAreaList(al);

	    xscatprintf(&report, " %s\r\r",print_ch(77,'-'));

	    // warning! do not ever use aka2str twice at once!
	    sprintf(linkAka, "%s", aka2str(link->hisAka));
	    w_log(LL_AREAFIX, "areafix: Available Area List from %s sent to %s", aka2str(uplink->hisAka), linkAka);
	}
    }

    if (report==NULL) {
	xstrcat(&report, "\r  no links for creating Available Area List\r");
	w_log(LL_AREAFIX, "areafix: no links for creating Available Area List");
    }
    return report;
}                                                                               

// subscribe if (act==0),  unsubscribe if (act==1), delete if (act==2)
int forwardRequestToLink (char *areatag, s_link *uplink, s_link *dwlink, int act) {
    s_message *msg;
    char *base, pass[]="passthrough";

    if (uplink->msg == NULL) {
	msg = makeMessage(uplink->ourAka, &(uplink->hisAka), config->sysop, 
        uplink->RemoteRobotName ? uplink->RemoteRobotName : "areafix",
        uplink->areaFixPwd ? uplink->areaFixPwd : "\x00", 1,
        config->areafixKillReports);
	msg->text = createKludges(config->disableTID,NULL, uplink->ourAka, &(uplink->hisAka),
                              versionStr);
	uplink->msg = msg;
    } else msg = uplink->msg;
	
    if (act==0) {
    if (getArea(config, areatag) == &(config->badArea)) {
        if(config->areafixQueueFile) {
            af_CheckAreaInQuery(areatag, &(uplink->hisAka), &(dwlink->hisAka), ADDFREQ);
        }
        else {
            base = uplink->msgBaseDir;
            if (config->createFwdNonPass==0) uplink->msgBaseDir = pass;
            // create from own address
            if (isOurAka(config,dwlink->hisAka)) {
                uplink->msgBaseDir = base;
            }
            strUpper(areatag);
            autoCreate(areatag, uplink->hisAka, &(dwlink->hisAka));
            uplink->msgBaseDir = base;
        }
    }
    xstrscat(&msg->text, "+", areatag, "\r", NULL);
    } else if (act==1) {
        xscatprintf(&(msg->text), "-%s\r", areatag);
    } else {
        // delete area
        if (uplink->advancedAreafix)
            xscatprintf(&(msg->text), "~%s\r", areatag);
        else
            xscatprintf(&(msg->text), "-%s\r", areatag);
    }
    return 0;
}

/*
int delLinkFromString(char **lineOut, char *line, char *linkAddr)
{
    char *startLink, *ptr, *tmp, *origLine, *comment, *eptr=NULL, *linkStr;
    unsigned int nodeAddr=0, rc=1, endLen=0; // length of string where link ends

    origLine = safe_strdup(line);
    startLink = line;
    tmp = line;
    // find comments
    for (comment = line; (comment = strchr(comment+1, CommentChar)) != NULL;)
	if (*(comment-1) == ' ' || *(comment-1) == '\t')
	    break;
    // make search string
    linkStr = safe_malloc(strlen(linkAddr)+3);
    strcpy(linkStr, linkAddr);
    ptr=strchr(linkStr, '.');
    if (ptr==NULL) {
	nodeAddr = 1;
        ptr=linkStr+strlen(linkStr);
    }
    strcpy(ptr, ".*");

    tmp = line;
    do {
	ptr = strstr(tmp+1, linkAddr);
	endLen = 0;
        if (ptr && comment && ptr>=comment) ptr = NULL;
        if (ptr) tmp = ptr + strlen(linkAddr);
	if (ptr && isspace(*(ptr-1))) {
            eptr = ptr+strlen(linkAddr);
	    if (isspace(*eptr) || *eptr=='\0' ||
                (nodeAddr && *eptr=='.' && eptr[1]=='0' && (isspace(eptr[2]) || eptr[2]=='\0'))) {
                startLink = ptr;
		endLen = eptr + 1 - line;
		rc = 0; // all ok
	    }
	}
	ptr = strstr(tmp+1, linkStr);
        if (ptr && comment && ptr>=comment) ptr = NULL;
        if (ptr) tmp = ptr + strlen(linkAddr);
	if (ptr && isspace(*(ptr-1))) {
	    eptr = ptr+strlen(linkStr);
            if (isspace(*eptr) || *eptr=='\0') {
                startLink = ptr;
		endLen = eptr + 1 - line;
		rc = 2; // found, but cannot unsubscribe
	    }
	}
    } while (!endLen && tmp);

    if (rc == 0) {
	eptr = tmp;
	if (*eptr == '.' && eptr[1] == '0') eptr+=2;
	if (*eptr && isspace(*eptr)) eptr++;
	endLen = eptr - line;
	ptr = line + endLen;
        while (ptr) {
            tmp = strseparate(&ptr, " \t"); // looking for link options...
            if (tmp == NULL)  break; // nothing found
            if (*tmp != '-') break; // this is not option
            else { // found link option
                // if (!strncasecmp(tmp, "-r", 2) ||
                //    !strncasecmp(tmp, "-w", 2) ||
                //    !strncasecmp(tmp, "-mn", 3) ||
                //   !strncasecmp(tmp, "-def", 4)) 
                {
                    endLen = ptr ? (ptr - line) : strlen(origLine);
                    continue;
                }
            }
        }
        nfree(*lineOut);
        *lineOut = (char *) safe_calloc(strlen(origLine) + 1, 1);
        strncpy(*lineOut, origLine, startLink - line);
        if (endLen < strlen(origLine))
	    strcpy(*lineOut+(startLink-line), origLine+endLen);
    }
    nfree(origLine);
    nfree(linkStr);
    return rc;
}
*/
int changeconfig(char *fileName, s_area *area, s_link *link, int action) {
    FILE *f_conf;
    char *cfgline=NULL, *token=NULL, *tmpPtr=NULL, *line=NULL, *buff=0;
    long endpos, cfglen;
    long strbeg = 0, strend = -1;
    int rc=0;
  
    e_changeConfigRet nRet = I_ERR;
    char *areaName = area->areaName;

    w_log(LL_FUNC,"areafix.c::changeconfig()");

    if (init_conf(fileName))
		return -1;

    w_log(LL_SRCLINE,"areafix.c:%u:changeconfig() action=%i",__LINE__,action);

    while ((cfgline = configline()) != NULL) {
        line = sstrdup(cfgline);
        line = trimLine(line);
        line = stripComment(line);
        if (line[0] != 0) {
            line = shell_expand(line);
            line = tmpPtr = vars_expand(line);
            token = strseparate(&tmpPtr, " \t");
            if (stricmp(token, "echoarea")==0) {
                token = strseparate(&tmpPtr, " \t"); 
                if (*token=='\"' && token[strlen(token)-1]=='\"' && token[1]) {
                    token++;
                    token[strlen(token)-1]='\0';
                }
                if (stricmp(token, areaName)==0) {
                    fileName = safe_strdup(getCurConfName());
                    strend = get_hcfgPos();
                    if(strbeg > strend) strbeg = 0;
                    break;
                }
            }
        }
        strbeg = get_hcfgPos();
        nfree(line);
        nfree(cfgline);
    }
    close_conf();
    nfree(line);
    if (strend == -1) { // impossible // error occurred
        nfree(cfgline);
        nfree(fileName);
        return -1;
    }
    if ((f_conf=fopen(fileName,"r+b")) == NULL)
    {
        if (!quiet) fprintf(stderr, "areafix: cannot open config file %s \n", fileName);
        nfree(cfgline);
        nfree(fileName);
        return 1;
    }
    nfree(fileName);

    w_log(LL_SRCLINE,"areafix.c:%u:changeconfig()", __LINE__);

    fseek(f_conf, 0L, SEEK_END);
    endpos = ftell(f_conf);
    cfglen = endpos - strend;
    line = (char*) smalloc((size_t) cfglen+1);
    fseek(f_conf, strend, SEEK_SET);
    cfglen = fread(line, sizeof(char), cfglen, f_conf);
    line[cfglen]='\0';
    fseek(f_conf, strbeg, SEEK_SET);
    setfsize( fileno(f_conf), strbeg );

    w_log(LL_SRCLINE,"areafix.c:%u:changeconfig()", __LINE__);

    switch (action) {
    case 0: // forward Request To Link
        if ((area->msgbType==MSGTYPE_PASSTHROUGH) && 
            (!config->areafixQueueFile) && 
            (area->downlinkCount==1) &&
            (area->downlinks[0]->link->hisAka.point == 0))
        {
            forwardRequestToLink(areaName, area->downlinks[0]->link, NULL, 0);
        }
    case 3: // add link to existing area
        xscatprintf(&cfgline, " %s", aka2str(link->hisAka));
        nRet = ADD_OK;
        break;
    case 1: // remove link from area
        if ((area->msgbType==MSGTYPE_PASSTHROUGH)
            && (area->downlinkCount==1) &&
            (area->downlinks[0]->link->hisAka.point == 0)) {
            forwardRequestToLink(areaName, area->downlinks[0]->link, NULL, 1);
        }
    case 7:
        if ((rc = DelLinkFromString(cfgline, link->hisAka)) == 1) {
            w_log(LL_ERR,"areafix: Unlink is not possible for %s from echo area %s",
                aka2str(link->hisAka), areaName);
            nRet = O_ERR;
        } else {
            nRet = DEL_OK;
        }
        break;
    case 2:
        //makepass(f, fileName, areaName);
    case 4: // delete area 
        nfree(cfgline);
        nRet = DEL_OK;
        break;
    case 5: // subscribe us to  passthrough
        if ( hpt_stristr(area->downlinks[0]->link->autoAreaCreateDefaults,
            "passthrough") )  {
            nRet = O_ERR;
            break;
        }   
    w_log(LL_SRCLINE,"areafix.c::changeconfig():%u",__LINE__);
        // get area string
        buff = makeAreaParam(area->downlinks[0]->link , areaName, NULL );
        nRet = ADD_OK;
    case 6: // make area pass. 
        
        if(action == 6) {
            buff = makeAreaParam(area->downlinks[0]->link , areaName, "passthrough" );
            nRet = DEL_OK;
        }
        // add all links
        token = NULL;
        token = strrchr(cfgline, '\"');
        if(!token) token = cfgline;
        token = strstr(token, aka2str(area->downlinks[0]->link->hisAka));
        if(!testAddr(token,area->downlinks[0]->link->hisAka))
            token = strstr(token+1, aka2str(area->downlinks[0]->link->hisAka));
        
        xstrcat( &buff, token-1); 
        nfree(cfgline);
        cfgline = buff;
        break;
    default: break;
    } // switch (action)
    if(cfgline) { // line not deleted
        fprintf(f_conf, "%s%s%s", cfgline, cfgEol(), line);
    } else {
        fprintf(f_conf, "%s", line);
    }
    fclose(f_conf);
    nfree(line);
    nfree(cfgline);
    w_log(LL_FUNC,"areafix.c::changeconfig() rc=%i", nRet);
    return nRet;
}

static int compare_links_priority(const void *a, const void *b) {
    int ia = *((int*)a);
    int ib = *((int*)b);
    if(config->links[ia].forwardAreaPriority < config->links[ib].forwardAreaPriority) return -1;
    else if(config->links[ia].forwardAreaPriority > config->links[ib].forwardAreaPriority) return 1;
    else return 0;
}

int forwardRequest(char *areatag, s_link *dwlink, s_link **lastRlink) {
    unsigned int i, rc = 1;
    s_link *uplink;
    int *Indexes;
    unsigned int Requestable = 0;

    /* From Lev Serebryakov -- sort Links by priority */
    Indexes = safe_malloc(sizeof(int)*config->linkCount);
    for (i = 0; i < config->linkCount; i++) {
	if (config->links[i].forwardRequests) Indexes[Requestable++] = i;
    }
    qsort(Indexes,Requestable,sizeof(Indexes[0]),compare_links_priority);
    i = 0;
    if(lastRlink) { // try to find next requestable uplink
        for (; i < Requestable; i++) {
            uplink = &(config->links[Indexes[i]]);
            if( addrComp(uplink->hisAka, (*lastRlink)->hisAka) == 0)
            {   // we found lastRequestedlink
                i++;   // let's try next link
                break;
            }
        }
    }
    for (; i < Requestable; i++) {
	uplink = &(config->links[Indexes[i]]);
    
    if(lastRlink) *lastRlink = uplink;

    if (uplink->forwardRequests && (uplink->LinkGrp) ?
        grpInArray(uplink->LinkGrp,dwlink->AccessGrp,dwlink->numAccessGrp) : 1) 
    {
        if ( (uplink->numDfMask) &&
            (tag_mask(areatag, uplink->dfMask, uplink->numDfMask))) 
        {
            rc = 2;
            continue;
        }
        if ( (uplink->denyFwdFile!=NULL) &&
            (IsAreaAvailable(areatag,uplink->denyFwdFile,NULL,0)))
        {
            rc = 2;
            continue;
        }
        if (uplink->forwardRequestFile!=NULL) {
            // first try to find the areatag in forwardRequestFile
            if (tag_mask(areatag, uplink->frMask, uplink->numFrMask) || 
                IsAreaAvailable(areatag,uplink->forwardRequestFile,NULL,0))
            {
                forwardRequestToLink(areatag,uplink,dwlink,0);
                rc = 0;
            }
            else  
            { rc = 2; }// found link with freqfile, but there is no areatag
        } else {
            rc = 0;
            if (uplink->numFrMask) // found mask
            { 
                if (tag_mask(areatag, uplink->frMask, uplink->numFrMask))
                    forwardRequestToLink(areatag,uplink,dwlink,0);
                else rc = 2;
            } else { // unconditional forward request
                if (dwlink->denyUFRA==0)
                    forwardRequestToLink(areatag,uplink,dwlink,0);
                else rc = 2;
            }
        }//(uplink->forwardRequestFile!=NULL) 
        if (rc==0) { // ?
            nfree(Indexes);
            return rc;
        }

    }// if (uplink->forwardRequests && (uplink->LinkGrp) ?
    }// for (i = 0; i < Requestable; i++) {
    
    // link with "forwardRequests on" not found
    nfree(Indexes);
    return rc;
}

/* test link for areas quantity limit exceed
 * return 0 if not limit exceed
 * else return not zero
 */
int limitCheck(s_link *link) {
    register unsigned int i,n;

    w_log(LL_FUNC,"areafix.c::limitCheck()");

    if (link->afixEchoLimit==0) return 0;
    for (i=n=0; i<config->echoAreaCount; i++)
	if (0==subscribeCheck(config->echoAreas[i], link))	
	    n++;
    i = n >= link->afixEchoLimit ;
    w_log(LL_FUNC,"areafix.c::limitCheck() rc=%u", i);
    return i;
}

int isPatternLine(char *s) {
    if (strchr(s,'*') || strchr(s,'?')) return 1;
    return 0;
}

void fixRules (s_link *link, s_area *area) {
    char *fileName = NULL, *fn, *fn1;

    if (!config->rulesDir) return;
    if (link->noRules) return;

    if (area->fileName) {
	fn = area->fileName;
	for (fn1 = fn; *fn1; fn1++) if (*fn1=='/' || *fn1=='\\') fn = fn1+1;
	xscatprintf(&fileName, "%s%s.rul", config->rulesDir, fn);
    } else {
	fn = makeMsgbFileName(config, area->areaName);
	xscatprintf(&fileName, "%s%s.rul", config->rulesDir, fn);
	nfree (fn); // allocated by makeMsgbFileName()
    }

    if (fexist(fileName)) {
	rulesCount++;
	rulesList = safe_realloc (rulesList, rulesCount * sizeof (char*));
	rulesList[rulesCount-1] = safe_strdup (area->areaName);
	// don't simply copy pointer because area may be
	// removed while processing other commands
    }
    nfree (fileName);
}

char *subscribe(s_link *link, char *cmd) {
    unsigned int i, rc=4, found=0, matched=0;
    char *line, *an=NULL, *report = NULL;
    s_area *area=NULL;

    w_log(LL_FUNC, "areafix::subscribe()");

    line = cmd;
	
    if (line[0]=='+') line++;
    while (*line==' ') line++;

    if (*line=='+') line++; while (*line==' ') line++;
	
    if (strlen(line)>60 || !isValidConference(line)) {
      w_log(LL_FUNC, "areafix::subscribe() FAILED (error request line)");
      return errorRQ(line);
    }

    for (i=0; !found && rc!=6 && i<config->echoAreaCount; i++) {
	area = &(config->echoAreas[i]);
	an = area->areaName;

	rc=subscribeAreaCheck(area, line, link);
	if (rc==4) continue;        /* not match areatag, try next */
	if (rc==1 && mandatoryCheck(*area, link)) rc = 5; /* mandatory area/group/link */

	if (rc!=0 && limitCheck(link)) rc = 6; /* areas limit exceed for link */

	switch (rc) {
	case 0:         /* already linked */
	    if (isPatternLine(line)) {
		matched = 1;
	    } else {
		xscatprintf(&report, " %s %s  already linked\r",
			    an, print_ch(49-strlen(an), '.'));
		w_log(LL_AREAFIX, "areafix: %s already linked to %s",
		      aka2str(link->hisAka), an);
		i = config->echoAreaCount;
	    }
	    break;
	case 1:         /* not linked */
        if( isOurAka(config,link->hisAka)) { 
           if(area->msgbType==MSGTYPE_PASSTHROUGH) {
              int state = 
                  changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,5);
              if( state == ADD_OK) {
                  af_CheckAreaInQuery(an, NULL, NULL, DELIDLE);
                  xscatprintf(&report," %s %s  added\r",an,print_ch(49-strlen(an),'.'));
                  w_log(LL_AREAFIX, "areafix: %s subscribed to %s",aka2str(link->hisAka),an);
              } else {
                  xscatprintf(&report, " %s %s  not subscribed\r",an,print_ch(49-strlen(an), '.'));
                  w_log(LL_AREAFIX, "areafix: %s not subscribed to %s , cause uplink",aka2str(link->hisAka),an);
                  w_log(LL_AREAFIX, "areafix: %s has \"passthrough\" in \"autoAreaCreateDefaults\" for %s", 
                                    an, aka2str(area->downlinks[0]->link->hisAka));
              }
           } else {  /* ??? (not passthrou echo) */
                     //  non-passthrough area for our aka means 
                     //  that we already linked to this area
               xscatprintf(&report, " %s %s  already linked\r",an, print_ch(49-strlen(an), '.'));
               w_log(LL_AREAFIX, "areafix: %s already linked to %s",aka2str(link->hisAka), an);
           }
        } else {
            if (changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,0)==ADD_OK) {
                addlink(link, area);
                fixRules (link, area);
                af_CheckAreaInQuery(an, NULL, NULL, DELIDLE);
                xscatprintf(&report," %s %s  added\r",an,print_ch(49-strlen(an),'.'));
                w_log(LL_AREAFIX, "areafix: %s subscribed to %s",aka2str(link->hisAka),an);
            } else {
                xscatprintf(&report," %s %s  error. report to sysop!\r",an,print_ch(49-strlen(an),'.'));
                w_log(LL_AREAFIX, "areafix: %s not subscribed to %s",aka2str(link->hisAka),an);
                w_log(LL_ERR, "areafix: can't write to config file: %s!", strerror(errno));
            }//if (changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,3)==0)
        }
	    if (!isPatternLine(line)) i = config->echoAreaCount;
	    break;
	case 6:         /* areas limit exceed for link */
	    break;
	default : // rc = 2 /* not access */
	    if (!area->hide && !isPatternLine(line)) {
		w_log(LL_AREAFIX, "areafix: area %s -- no access for %s",
		      an, aka2str(link->hisAka));
		xscatprintf(&report," %s %s  no access\r", an,
			    print_ch(49-strlen(an), '.'));
		found=1;
	    }
	    if (area->hide && !isPatternLine(line)) found=1;
	    break;
	}
    }

    if (rc!=0 && limitCheck(link)) rc = 6; /*double!*/ /* areas limit exceed for link */

    if (rc==4 && !isPatternLine(line) && !found) { /* rc not equal 4 there! */
	if (link->denyFRA==0) {
	    // try to forward request
	    if ((rc=forwardRequest(line, link, NULL))==2) {
		xscatprintf(&report, " %s %s  no uplinks to forward\r",
			    line, print_ch(49-strlen(line), '.'));
		w_log( LL_AREAFIX, "areafix: %s - no uplinks to forward", line);
	    }
	    else if (rc==0) {
		xscatprintf(&report, " %s %s  request forwarded\r",
			    line, print_ch(49-strlen(line), '.'));
		w_log( LL_AREAFIX, "areafix: %s - request forwarded", line);
        if( !config->areafixQueueFile && isOurAka(config,link->hisAka)==0)
        {
            area = getArea(config, line);
            if ( !isLinkOfArea(link, area) ) {
                if(changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,3)==ADD_OK) {
                    addlink(link, area);
                    fixRules (link, area);
                    w_log( LL_AREAFIX, "areafix: %s subscribed to area %s",
                        aka2str(link->hisAka),line);
                } else {
                    xscatprintf( &report," %s %s  error. report to sysop!\r",
                        an, print_ch(49-strlen(an),'.') );
                    w_log( LL_AREAFIX, "areafix: %s not subscribed to %s",
                        aka2str(link->hisAka),an);
                    w_log(LL_ERR, "areafix: can't change config file: %s!", strerror(errno));
                }
            } else w_log( LL_AREAFIX, "areafix: %s already subscribed to area %s",
                aka2str(link->hisAka), line );
            
        } else {
            // fixRules (link, line); there is no functionality  to send rules of area
            //                        that  not exist in config.
            //                        Patches are welcome!
        }
        }
	}
    }

    if (rc == 6) {   /* areas limit exceed for link */
	w_log( LL_AREAFIX,"areafix: area %s -- no access (full limit) for %s",
	      line, aka2str(link->hisAka));
	xscatprintf(&report," %s %s  no access (full limit)\r",
		    line, print_ch(49-strlen(line), '.'));
    }

    if (matched) {
	if (report == NULL)
	    w_log (LL_AREAFIX, "areafix: all areas matching %s are already linked", line);
	xscatprintf(&report, "All %sareas matching %s are already linked\r", report ? "other " : "", line);
    }
    else if ((report == NULL && found==0) || (found && area->hide)) {
	xscatprintf(&report," %s %s  not found\r",line,print_ch(49-strlen(line),'.'));
	w_log( LL_AREAFIX, "areafix: area %s is not found",line);
    }
    w_log(LL_FUNC, "areafix::subscribe() OK");
    return report;
}

char *errorRQ(char *line) {
    char *report = NULL;
    
    if (strlen(line)>48) {
	xstrscat(&report, " ", line, " .......... error line\r", NULL);
    }
    else xscatprintf(&report, " %s %s  error line\r",
		    line, print_ch(49-strlen(line),'.'));
    return report;
}

char *do_delete(s_link *link, s_area *area) {
    char *report = NULL, *an = area->areaName;
    unsigned int i=0;

    if(!link) 
    {
        link = getLinkFromAddr(config, *area->useAka);
        while( !link && i < config->addrCount )
        {
            link = getLinkFromAddr( config, config->addr[i] );
            i++;
        }
        if(!link) return NULL;
    }
    /* unsubscribe from downlinks */
    xscatprintf(&report, " %s %s  deleted\r", an, print_ch(49-strlen(an), '.'));
    for (i=0; i<area->downlinkCount; i++) {
	if (addrComp(area->downlinks[i]->link->hisAka, link->hisAka))
	    forwardRequestToLink(an, area->downlinks[i]->link, NULL, 2);
    }
    /* remove area from config-file */
    if( changeconfig ((cfgFile) ? cfgFile : getConfigFileName(),  area, link, 4) != DEL_OK) {
       w_log( LL_AREAFIX, "areafix: can't remove area from config: %s", strerror(errno));
    }

    /* delete msgbase and dupebase for the area */
    if (area->msgbType!=MSGTYPE_PASSTHROUGH)
	MsgDeleteBase(area->fileName, (word) area->msgbType);
    if (area->dupeCheck != dcOff) {
	char *dupename = createDupeFileName(area);
	if (dupename) {
	    unlink(dupename);
	    nfree(dupename);
	}
    }

    w_log( LL_AREAFIX, "areafix: area %s deleted by %s",
                  an, aka2str(link->hisAka));

    /* delete the area from in-core config */
    for (i=0; i<area->downlinkCount; i++)
	nfree(area->downlinks[i]);
    nfree(area->downlinks);
    area->downlinkCount = 0;
    for (i=0; i<config->echoAreaCount; i++)
	if (stricmp(config->echoAreas[i].areaName, an)==0)
	    break;
    if (i<config->echoAreaCount && area==&(config->echoAreas[i])) {
	nfree(area->areaName);
	nfree(area->fileName);
	nfree(area->description);
	nfree(area->group);
	for (; i<config->echoAreaCount-1; i++)
	    memcpy(&(config->echoAreas[i]), &(config->echoAreas[i+1]),
	           sizeof(s_area));
	config->echoAreaCount--;
    RebuildEchoAreaTree(config);
    }
    return report;
}

char *delete(s_link *link, char *cmd) {
    int rc;
    char *line, *report = NULL, *an;
    s_area *area;

    for (line = cmd + 1; *line == ' ' || *line == '\t'; line++);

    if (*line == 0) return errorRQ(cmd);

    area = getArea(config, line);
    if (area == &(config->badArea)) {
	xscatprintf(&report, " %s %s  not found\r", line, print_ch(49-strlen(line), '.'));
	w_log(LL_AREAFIX, "areafix: area %s is not found", line);
	return report;
    }
    rc = subscribeCheck(*area, link);
    an = area->areaName;

    switch (rc) {
    case 0:
	break;
    case 1:
	xscatprintf(&report, " %s %s  not linked\r", an, print_ch(49-strlen(an), '.'));
	w_log(LL_AREAFIX, "areafix: area %s is not linked to %s",
	      an, aka2str(link->hisAka));
	return report;
    case 2:
	xscatprintf(&report, " %s %s  no access\r", an, print_ch(49-strlen(an), '.'));
	w_log(LL_AREAFIX, "areafix: area %s -- no access for %s", an, aka2str(link->hisAka));
	return report;
    }
    if (link->LinkGrp == NULL || (area->group && strcmp(link->LinkGrp, area->group))) {
	xscatprintf(&report, " %s %s  delete not allowed\r",
		    an, print_ch(49-strlen(an), '.'));
	w_log(LL_AREAFIX, "areafix: area %s delete not allowed for %s",
	      an, aka2str(link->hisAka));
	return report;
    }
    return do_delete(link, area);
}

char *unsubscribe(s_link *link, char *cmd) {
    unsigned int i, rc = 2, j, from_us=0, matched = 0;
    char *line, *an, *report = NULL;
    s_area *area;
	
    line = cmd;
	
    if (line[1]=='-') return NULL;
    line++;
    while (*line==' ') line++;
	
    for (i = 0; i< config->echoAreaCount; i++) {
	area = &(config->echoAreas[i]);
	an = area->areaName;

	rc = subscribeAreaCheck(area, line, link);
	if (rc==4) continue;
	if (rc==0 && mandatoryCheck(*area,link)) rc = 5;

	if (isOurAka(config,link->hisAka))
    { 
        from_us = 1;
        rc = area->msgbType == MSGTYPE_PASSTHROUGH ? 1 : 0 ;
    }

	switch (rc) {
	case 0:
	    if (from_us == 0) {
                unsigned int k;
                for (k=0; k<area->downlinkCount; k++)
                    if (addrComp(link->hisAka, area->downlinks[k]->link->hisAka)==0 &&
                        area->downlinks[k]->defLink)
                        return do_delete(link, area);
                removelink(link, area);
                if ((area->msgbType == MSGTYPE_PASSTHROUGH) &&
                    (area->downlinkCount == 1) &&
                    (area->downlinks[0]->link->hisAka.point == 0))
                {
                    if(config->areafixQueueFile)
                    {
                        af_CheckAreaInQuery(an, &(area->downlinks[0]->link->hisAka), NULL, ADDIDLE);
                        j = changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,7);
                    }
                    else
                    {
                        j = changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,1);
                    }
                } else {
                
                    j = changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,7);
                }
                if (j != DEL_OK) {
                    w_log(LL_AREAFIX, "areafix: %s doesn't unlinked from %s",
                          aka2str(link->hisAka), an);
                } else
                    w_log(LL_AREAFIX,"areafix: %s unlinked from %s",aka2str(link->hisAka),an);
            } else { // unsubscribing from own address
                if ((area->downlinkCount==1) &&
                    (area->downlinks[0]->link->hisAka.point == 0)) {
                    if(config->areafixQueueFile) {
                        af_CheckAreaInQuery(an, &(area->downlinks[0]->link->hisAka), NULL, ADDIDLE);
                    } else {
                        forwardRequestToLink(area->areaName,
                                             area->downlinks[0]->link, NULL, 1);
                    }
                }
                j = changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,6);
            }
	    if (j == DEL_OK)
                xscatprintf(&report," %s %s  unlinked\r",an,print_ch(49-strlen(an),'.'));
            else
                xscatprintf(&report," %s %s  error. report to sysop!\r",
                            an, print_ch(49-strlen(an),'.'));
            break;
        case 1:
            if (isPatternLine(line)) {
                matched = 1;
                continue;
            }
            if (area->hide) {
                i = config->echoAreaCount;
                break;
            }
            xscatprintf(&report, " %s %s  not linked\r",
                        an, print_ch(49-strlen(an), '.'));
            w_log(LL_AREAFIX, "areafix: area %s is not linked to %s",
                  area->areaName, aka2str(link->hisAka));
            break;
        case 5:
	    xscatprintf(&report, " %s %s  unlink is not possible\r",
			an, print_ch(49-strlen(an), '.'));
	    w_log(LL_AREAFIX, "areafix: area %s -- unlink is not possible for %s",
		  area->areaName, aka2str(link->hisAka));
	    break;
	default:
	    break;
	}
    }
    if(config->areafixQueueFile)
    report = af_Req2Idle(line, report, link->hisAka);
    if (report == NULL) {
        if (matched) {
            xscatprintf(&report, " %s %s  no areas to unlink\r",
                line, print_ch(49-strlen(line), '.'));
            w_log(LL_AREAFIX, "areafix: no areas to unlink");
        } else {
            xscatprintf(&report, " %s %s  not found\r",
                line, print_ch(49-strlen(line), '.'));
            w_log(LL_AREAFIX, "areafix: area %s is not found", line);
        }
    }
    return report;
}


char *pause_link(s_link *link)
{
   char *tmp, *report = NULL;
   
   if ((link->Pause & EPAUSE) != EPAUSE) {
      if (Changepause((cfgFile) ? cfgFile : getConfigFileName(), link, 0,EPAUSE) == 0)
         return NULL;
   }
   xstrcat(&report, " System switched to passive\r");
   tmp = linked (link);
   xstrcat(&report, tmp);
   nfree(tmp);
   
   return report;
}
char *resume_link(s_link *link)
{
    char *tmp, *report = NULL;
    
    if ((link->Pause & EPAUSE) == EPAUSE) {
	if (Changepause((cfgFile) ? cfgFile : getConfigFileName(), link,0,EPAUSE) == 0)
	    return NULL;
    }

    xstrcat(&report, " System switched to active\r");
    tmp = linked (link);
    xstrcat(&report, tmp);
    nfree(tmp);

    return report;
}

char *info_link(s_link *link)
{
    char *report=NULL, *ptr, linkAka[SIZE_aka2str];
    char hisAddr[]="Your address: ";
    char ourAddr[]="AKA used here: ";
    char Arch[]="Compression: ";
	unsigned int i;
    
    sprintf(linkAka,aka2str(link->hisAka));
    xscatprintf(&report, "Here is some information about our link:\r\r %s%s\r%s%s\r  %s", hisAddr, linkAka, ourAddr, aka2str(*link->ourAka), Arch);
    
    if (link->packerDef==NULL) 
	xscatprintf(&report, "No packer (");
    else
	xscatprintf(&report, "%s (", link->packerDef->packer);
    
    for (i=0; i < config->packCount; i++)
	xscatprintf(&report, "%s%s", config->pack[i].packer,
		    (i+1 == config->packCount) ? "" : ", ");
    
    xscatprintf(&report, ")\r\rYour system is %s\r", ((link->Pause & EPAUSE) == EPAUSE)?"passive":"active");
    ptr = linked (link);
    xstrcat(&report, ptr);
    nfree(ptr);
    w_log(LL_AREAFIX, "areafix: link information sent to %s", aka2str(link->hisAka));
    return report;
}

int repackEMMsg(HMSG hmsg, XMSG xmsg, s_area *echo, s_arealink *arealink)
{
   s_message    msg;
   UINT32       j=0;
   s_seenBy     *seenBys = NULL, *path = NULL;
   UINT         seenByCount = 0, pathCount = 0;
   s_arealink   **links;

   links = (s_arealink **) scalloc(2, sizeof(s_arealink*));
   if (links==NULL) exit_hpt("out of memory",1);
   links[0] = arealink;
   
   makeMsg(hmsg, xmsg, &msg, echo, 1);

   //translating name of the area to uppercase
   while (msg.text[j] != '\r') {msg.text[j]=(char)toupper(msg.text[j]);j++;}

   if (strncmp(msg.text+j+1,"NOECHO",6)==0) {
       freeMsgBuffers(&msg);
       nfree(links);
       return 0;
   }

   createSeenByArrayFromMsg(echo, &msg, &seenBys, &seenByCount);
   createPathArrayFromMsg(&msg, &path, &pathCount);

   forwardToLinks(&msg, echo, links, &seenBys, &seenByCount, &path, &pathCount);

   // mark msg as sent and scanned
   xmsg.attr |= MSGSENT;
   xmsg.attr |= MSGSCANNED;
   MsgWriteMsg(hmsg, 0, &xmsg, NULL, 0, 0, 0, NULL);

   freeMsgBuffers(&msg);
   nfree(links);
   nfree(seenBys);
   nfree(path);

   return 1;
}

int rescanEMArea(s_area *echo, s_arealink *arealink, long rescanCount)
{
   HAREA area;
   HMSG  hmsg;
   XMSG  xmsg;
   dword highestMsg, i;
   unsigned int rc=0;

   area = MsgOpenArea((UCHAR *) echo->fileName, MSGAREA_NORMAL, /*echo -> fperm, 
   echo -> uid, echo -> gid,*/ (word)(echo->msgbType | MSGTYPE_ECHO));
   if (area != NULL) {
       //      i = highWaterMark = MsgGetHighWater(area);
       i = 0;
       highestMsg    = MsgGetHighMsg(area);

       // if rescanCount == -1 all mails should be rescanned
       if ((rescanCount == -1) || (rescanCount > (long)highestMsg))
	   rescanCount = highestMsg;

       while (i <= highestMsg) {
	   if (i > highestMsg - rescanCount) { // honour rescanCount paramater
	       hmsg = MsgOpenMsg(area, MOPEN_RW, i);
	       if (hmsg != NULL) {     // msg# does not exist
		   MsgReadMsg(hmsg, &xmsg, 0, 0, NULL, 0, NULL);
		   rc += repackEMMsg(hmsg, xmsg, echo, arealink);
		   MsgCloseMsg(hmsg);
	       }
	   }
	   i++;
       }

       MsgSetHighWater(area, i);

       MsgCloseArea(area);
       closeOpenedPkt();
      
   } else w_log(LL_ERR, "Could not open %s: %s", echo->fileName, strerror(errno));

   return rc;
}

char *rescan(s_link *link, char *cmd) {
    unsigned int i, c, rc = 0;
    long rescanCount = -1, rcc;
    char *report = NULL, *line, *countstr, *an, *end;
    s_area *area;
    s_arealink *arealink;
    
    line = cmd;
    if (strncasecmp(cmd, "%rescan", 7)==0) line += strlen("%rescan");
    
    if (*line == 0) return errorRQ(cmd);
    
    while (*line && (*line == ' ' || *line == '\t')) line++;
    
    if (*line == 0) return errorRQ(cmd);

    countstr = line;
    while (*countstr && (!isspace(*countstr))) countstr++; // skip areatag
    while (*countstr && (*countstr == ' ' || *countstr == '\t')) countstr++;
    if (strncasecmp(countstr, "/R",2)==0) {
	countstr += 2;
	if (*countstr == '=') countstr++;
    }
	
    if (*countstr != '\0') {
	rescanCount = strtol(countstr, NULL, 10);
    }
    
    end = strpbrk(line, " \t");
    if (end) *end = 0;
    
    if (*line == 0) return errorRQ(cmd);

    for (i=c=0; i<config->echoAreaCount; i++) {
	rc=subscribeAreaCheck(&(config->echoAreas[i]), line, link);
	if (rc == 4) continue;

	area = &(config->echoAreas[i]);
	an = area->areaName;

	switch (rc) {
	case 0:
	    if (area->msgbType == MSGTYPE_PASSTHROUGH) {
		xscatprintf(&report," %s %s  no rescan possible\r",
			    an, print_ch(49-strlen(an), '.'));
		w_log(LL_AREAFIX, "areafix: %s area no rescan possible to %s",
		      an, aka2str(link->hisAka));
	    } else {

		arealink = getAreaLink(area, link->hisAka);
		if (arealink->export) {
		    rcc = rescanEMArea(area, arealink, rescanCount);
		    tossTempOutbound(config->tempOutbound);
		} else {
		    rcc = 0;
		    xscatprintf(&report," %s %s  no access to export\r",
				an, print_ch(49-strlen(an), '.'));
		    w_log(LL_AREAFIX, "areafix: %s -- no access to export for %s",
			  an, aka2str(link->hisAka));
		}
		xscatprintf(&report," %s %s  rescanned %lu mails\r",
			    an, print_ch(49-strlen(an), '.'), rcc);
		w_log(LL_AREAFIX,"areafix: %s rescanned %lu mails to %s",
		      an, rcc, aka2str(link->hisAka));
	    }
	    break;
	case 1: if (isPatternLine(line)) continue;
	    w_log(LL_AREAFIX, "areafix: %s area not linked for rescan to %s",
		  area->areaName, aka2str(link->hisAka));
	    xscatprintf(&report, " %s %s  not linked for rescan\r",
			an, print_ch(49-strlen(an), '.'));
	    break;
	default: w_log(LL_AREAFIX, "areafix: %s area not access for %s",
		       area->areaName, aka2str(link->hisAka));
	    break;
	}
    }
    if (report == NULL) {
	xscatprintf(&report," %s %s  not linked for rescan\r",
		    line, print_ch(49-strlen(line), '.'));
	w_log(LL_AREAFIX, "areafix: %s area not linked for rescan", line);
    }
    return report;
}

char *add_rescan(s_link *link, char *line) {
    char *report=NULL, *line2=NULL, *p;

    if (*line=='+') line++; while (*line==' ') line++;

    p = hpt_stristr(line, " /R");
    *p = '\0';

    report = subscribe (link, line);
    *p = ' ';

    xstrscat(&line2,"%rescan ", line, NULL);
    xstrcat(&report, rescan(link, line2));
    nfree(line2);
    *p = '\0';
    
    return report;
}

int tellcmd(char *cmd) {
    char *line;

    if (strncmp(cmd, "* Origin:", 9) == 0) return NOTHING;

    line = cmd;
    if (line && *line && (line[1]==' ' || line[1]=='\t')) return ERROR;

    switch (line[0]) {
    case '%': 
	line++;
	if (*line == '\000') return ERROR;
	if (strncasecmp(line,"list",4)==0) return LIST;
	if (strncasecmp(line,"help",4)==0) return HELP;
	if (strncasecmp(line,"avail",5)==0) return AVAIL;
	if (strncasecmp(line,"all",3)==0) return AVAIL;
	if (strncasecmp(line,"unlinked",8)==0) return UNLINK;
	if (strncasecmp(line,"linked",6)==0) return QUERY;
	if (strncasecmp(line,"query",5)==0) return QUERY;
	if (strncasecmp(line,"pause",5)==0) return PAUSE;
	if (strncasecmp(line,"resume",6)==0) return RESUME;
	if (strncasecmp(line,"info",4)==0) return INFO;
	if (strncasecmp(line, "rescan", 6)==0) {
	    if (line[6] == '\0') {
		rescanMode=1;
		return NOTHING;
	    } else {
		return RESCAN;
	    }
	}
	return ERROR;
    case '\001': return NOTHING;
    case '\000': return NOTHING;
    case '-'  :
	if (line[1]=='-' && line[2]=='-') return DONE;
	if (line[1]=='\000') return ERROR;
	if (strchr(line,' ') || strchr(line,'\t')) return ERROR;
	return DEL;
    case '~'  : return REMOVE;
    case '+':
	if (line[1]=='\000') return ERROR;
    default:
	if (hpt_stristr(line, " /R")!=NULL) return ADD_RSC; // add & rescan
	return ADD;
    }
//	return 0; - Unreachable
}

char *processcmd(s_link *link, char *line, int cmd) {
	
    char *report;

    w_log(LL_FUNC,"areafix.c::processcmd()");

    switch (cmd) {

    case NOTHING: return NULL;

    case DONE: RetFix=DONE;
	return NULL;

    case LIST: report = list (link, line);
	RetFix=LIST;
	break;
    case HELP: report = help (link);
	RetFix=HELP;
	break;
    case ADD: report = subscribe (link, line);
	RetFix=ADD;
	break;
    case DEL: report = unsubscribe (link, line);
	RetFix=STAT;
	break;
    case REMOVE: report = delete (link, line);
	RetFix=STAT;
	break;
    case AVAIL: report = available (link, line);
	RetFix=AVAIL;
	break;
    case UNLINK: report = unlinked (link);
	RetFix=UNLINK;
	break;
    case QUERY: report = linked (link);
	RetFix=QUERY;
	break;
    case PAUSE: report = pause_link (link);
	RetFix=PAUSE;
	break;
    case RESUME: report = resume_link (link);
	RetFix=RESUME;
	break;
    case INFO: report = info_link(link);
	RetFix=INFO;
	break;
    case RESCAN: report = rescan(link, line);
	RetFix=STAT;
	break;
    case ADD_RSC: report = add_rescan(link, line);
	RetFix=STAT;
	break;
    case ERROR: report = errorRQ(line);
	RetFix=STAT;
	break;
    default: return NULL;
    }
    w_log(LL_FUNC,"areafix.c::processcmd() OK");
    return report;
}

void preprocText(char *split, s_message *msg)
{
    char *orig = (config->areafixOrigin) ? config->areafixOrigin : config->origin;

    msg->text = createKludges(config->disableTID,NULL, &msg->origAddr, 
                              &msg->destAddr, versionStr);
    xstrcat(&(msg->text), "\001FLAGS NPD\r");
    xscatprintf(&split, "\r--- %s areafix\r", versionStr);
    if (orig) {
	xscatprintf(&split, " * Origin: %s (%s)\r", orig, aka2str(msg->origAddr));
    }
    xstrcat(&(msg->text), split);
    msg->textLength=(int)strlen(msg->text);
    nfree(split);
}

char *textHead(void)
{
    char *text_head = NULL;
    
    xscatprintf(&text_head, " Area%sStatus\r",	print_ch(48,' '));
    xscatprintf(&text_head, " %s  -------------------------\r",print_ch(50, '-')); 
    return text_head;
}

char *areaStatus(char *report, char *preport)
{
    if (report == NULL) report = textHead();
    xstrcat(&report, preport);
    nfree(preport);
    return report;
}

/* report already nfree() after this function */
void RetMsg(s_message *msg, s_link *link, char *report, char *subj)
{
    char *tab = config->intab, *text, *split, *p, *newsubj = NULL;
    char splitted[]=" > message splitted...";
    char *splitStr = config->areafixSplitStr;
    int len, msgsize = config->areafixMsgSize * 1024, partnum=0;
    s_message *tmpmsg;

    config->intab = NULL;

    text = report;

    while (text) {

	len = strlen(text);
	if (msgsize == 0 || len <= msgsize) {
	    split = text;
	    text = NULL;
	    if (partnum) { /* last part of splitted msg */
		partnum++;
		xstrcat(&text,split);
		split = text;
		text = NULL;
		nfree(report);
	    }
	} else {
	    p = text + msgsize;
	    while (*p != '\r') p--;
	    *p = '\000';
	    len = p - text;
	    split = (char*)safe_malloc(len+strlen(splitStr ? splitStr : splitted)+3+1);
	    memcpy(split,text,len);
	    strcpy(split+len,"\r\r");
	    strcat(split, (splitStr) ? splitStr : splitted);
	    strcat(split,"\r");
	    text = p+1;
	    partnum++;
	}

	if (partnum) xscatprintf(&newsubj, "%s (%d)", subj, partnum);
	else newsubj = subj;

	tmpmsg = makeMessage(link->ourAka, &(link->hisAka),
			     msg->toUserName,
			     msg->fromUserName, newsubj, 1,
                 config->areafixKillReports);

	preprocText(split, tmpmsg);
	processNMMsg(tmpmsg, NULL, getNetMailArea(config,config->robotsArea),
		     0, MSGLOCAL);

	closeOpenedPkt();
	freeMsgBuffers(tmpmsg);
	nfree(tmpmsg);
	if (partnum) nfree(newsubj);
    }

    config->intab = tab;
}

void RetRules (s_message *msg, s_link *link, char *areaName)
{
    FILE *f;
    char *fileName = NULL, *fn, *fn1;
    char *text, *subj=NULL;
    long len;
    s_area *area;
    int nrul;


    if ((area=getArea(config, areaName)) == &(config->badArea)) {
	w_log(LL_ERR, "areafix.c::RetRules(): can't find area '%s'", areaName);
	return;
    }

    if (area->fileName) {
	fn = area->fileName;
	for (fn1 = fn; *fn1; fn1++) if (*fn1=='/' || *fn1=='\\') fn = fn1+1;
	xscatprintf(&fileName, "%s%s.rul", config->rulesDir, fn);
    } else {
	fn = makeMsgbFileName(config, area->areaName);
	xscatprintf(&fileName, "%s%s.rul", config->rulesDir, fn);
	nfree (fn); // allocated by makeMsgbFileName()
    }

    for (nrul=0; nrul<=9 && (f = fopen (fileName, "rb")); nrul++) {

	len = fsize (fileName);
	text = safe_malloc (len+1);
	fread (text, len, 1, f);
	fclose (f);

	text[len] = '\0';

	if (nrul==0) {
	    xscatprintf(&subj, "Rules of %s", areaName);
	    w_log(LL_AREAFIX, "areafix: send '%s' as rules for area '%s'",
		  fileName, areaName);
	} else {
	    xscatprintf(&subj, "Echo related text #%d of %s", nrul, areaName);
	    w_log(LL_AREAFIX, "areafix: send '%s' as text %d for area '%s'",
		  fileName, nrul, areaName);
	}

	RetMsg(msg, link, text, subj);

	nfree (subj);
	//nfree (text); don't free text because RetMsg() free it

	fileName[strlen(fileName)-1] = nrul+'1';
    }

    if (nrul==0) { // couldn't open any rules file while first one exists!
	w_log(LL_ERR, "areafix: can't open file '%s' for reading: %s", fileName, strerror(errno));
    }
    nfree (fileName);

}

void sendAreafixMessages()
{
    s_link *link = NULL;
    s_message *linkmsg;
    unsigned int i;

    for (i = 0; i < config->linkCount; i++) {
        if (config->links[i].msg == NULL) continue;
        link = &(config->links[i]);
        linkmsg = link->msg;
        
        xscatprintf(&(linkmsg->text), " \r--- %s areafix\r", versionStr);
        linkmsg->textLength = strlen(linkmsg->text);
        
        w_log(LL_AREAFIX, "areafix: write netmail msg for %s", aka2str(link->hisAka));
        
        processNMMsg(linkmsg, NULL, getNetMailArea(config,config->robotsArea),
            0, MSGLOCAL);
        
        closeOpenedPkt();
        freeMsgBuffers(linkmsg);
        nfree(linkmsg);
        link->msg = NULL;
    }
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

int processAreaFix(s_message *msg, s_pktHeader *pktHeader, unsigned force_pwd)
{
    unsigned int security=1, notforme = 0;
    s_link *link = NULL;
    s_link *tmplink = NULL;
    //s_message *linkmsg;
    s_pktHeader header;
    char *token, *textBuff, *report=NULL, *preport = NULL;
    int nr;

    w_log(LL_FUNC, "areafix.c::processAreaFix()");

    RetFix = NOTHING;

    // 1st security check
    if (pktHeader) security=addrComp(msg->origAddr, pktHeader->origAddr);
    else {
	makePktHeader(NULL, &header);
	pktHeader = &header;
	pktHeader->origAddr = msg->origAddr;
	pktHeader->destAddr = msg->destAddr;
	security = 0;
    }

    if (security) security=1; /* different pkt and msg addresses */

    // find link
    link=getLinkFromAddr(config, msg->origAddr);

    // if keyword allowPktAddrDiffer for this link is on,
    // we allow the addresses in PKT and MSG header differ
    if (link!=NULL)
	if (link->allowPktAddrDiffer == pdOn)
	    security = 0;  /* OK */

    // is this for me?
    if (link!=NULL)	notforme=addrComp(msg->destAddr, *link->ourAka);
    else if (!security) security=4; // link == NULL; /* unknown system */
	
    if (notforme && !security) security=5; // message to wrong AKA

#if 0 // we're process only our messages here
    // ignore msg for other link (maybe this is transit...)
    if (notforme || (link==NULL && security==1)) {
        w_log(LL_FUNC, "areafix.c::processAreaFix() call processNMMsg() and return");
	nr = processNMMsg(msg, pktHeader, NULL, 0, 0);
	closeOpenedPkt();
	return nr;
    }
#endif

    // 2nd security check. link, areafixing & password.
    if (!security && !force_pwd) {
	if (link->AreaFix==1) {
	    if (link->areaFixPwd!=NULL) {
		if (stricmp(link->areaFixPwd,msg->subjectLine)==0) security=0;
		else security=3; /* password error */
	    }
	} else security=2; /* areafix is turned off */
    }

    if (!security) {
	textBuff = msg->text;
	token = strseparate (&textBuff, "\n\r");
	while(token != NULL) {
	    while ((*token == ' ') || (*token == '\t')) token++;
	    while(isspace(token[strlen(token)-1])) token[strlen(token)-1]='\0';
	    preport = processcmd( link, token, tellcmd (token) );
	    if (preport != NULL) {
		switch (RetFix) {
		case LIST:
		    RetMsg(msg, link, preport, "areafix reply: list request");
		    break;
		case HELP:
		    RetMsg(msg, link, preport, "areafix reply: help request");
		    break;
		case ADD:
		    report = areaStatus(report, preport);
		    if (rescanMode) {
			preport = processcmd( link, token, RESCAN );
			if (preport != NULL)
			    report = areaStatus(report, preport);
		    }
		    break;
		case AVAIL:
		    RetMsg(msg, link, preport, "areafix reply: available areas");
		    break;
		case UNLINK:
		    RetMsg(msg, link, preport, "areafix reply: unlinked request");
		    break;
		case QUERY:
		    RetMsg(msg, link, preport, "areafix reply: linked request");
		    break;
		case PAUSE:
		    RetMsg(msg, link, preport, "areafix reply: node change request");
		    break;
		case RESUME:
		    RetMsg(msg, link, preport, "areafix reply: node change request");
		    break;
		case INFO:
		    RetMsg(msg, link, preport, "areafix reply: link information");
		    break;
		case STAT:
		    report = areaStatus(report, preport);
		    break;
		default: break;
		}
	    } /* end if (preport != NULL) */
	    token = strseparate (&textBuff, "\n\r");
	    if (RetFix==DONE) token=NULL;
	} /* end while (token != NULL) */
    } else {
	if (link == NULL) {
	    tmplink = (s_link*) safe_malloc(sizeof(s_link));
	    memset(tmplink, '\0', sizeof(s_link));
	    tmplink->ourAka = &(msg->destAddr);
	    tmplink->hisAka.zone = msg->origAddr.zone;
	    tmplink->hisAka.net = msg->origAddr.net;
	    tmplink->hisAka.node = msg->origAddr.node;
	    tmplink->hisAka.point = msg->origAddr.point;
	    link = tmplink;
	}
	// security problem
		
	switch (security) {
	case 1:
	    xscatprintf(&report, " \r different pkt and msg addresses\r");
	    break;
	case 2:
	    xscatprintf(&report, " \r areafix is turned off\r");
	    break;
	case 3:
	    xscatprintf(&report, " \r password error\r");
	    break;
	case 4:
	    xscatprintf(&report, " \r your system is unknown\r");
	    break;
	case 5:
	    xscatprintf(&report, " \r message sent to wrong AKA\r");
	    break;
	default:
	    xscatprintf(&report, " \r unknown error. mail to sysop.\r");
	    break;
	}

	RetMsg(msg, link, report, "areafix reply: security violation");
	w_log(LL_AREAFIX, "areafix: security violation from %s", aka2str(link->hisAka));
	nfree(tmplink);
        w_log(LL_FUNC, "areafix.c::processAreaFix() rc=1");
	return 1;
    }

    if ( report != NULL ) {
	if (config->areafixQueryReports) {
	    preport = linked (link);
	    xstrcat(&report, preport);
	    nfree(preport);
	}
	RetMsg(msg, link, report, "areafix reply: node change request");
    }

    if (rulesCount) {
        for (nr=0; nr < rulesCount; nr++) {
            if (rulesList && rulesList[nr]) {
                RetRules (msg, link, rulesList[nr]);
                nfree (rulesList[nr]);
            }
	}
	nfree (rulesList);
    }

    w_log(LL_AREAFIX, "areafix: successfully done for %s",aka2str(link->hisAka));

    // send msg to the links (forward requests to areafix)
    sendAreafixMessages();
    w_log(LL_FUNC, "areafix.c::processAreaFix() rc=1");
    return 1;
}

void MsgToStruct(HMSG SQmsg, XMSG xmsg, s_message *msg)
{
    // convert header
    msg->attributes  = xmsg.attr;

    msg->origAddr.zone  = xmsg.orig.zone;
    msg->origAddr.net   = xmsg.orig.net;
    msg->origAddr.node  = xmsg.orig.node;
    msg->origAddr.point = xmsg.orig.point;

    msg->destAddr.zone  = xmsg.dest.zone;
    msg->destAddr.net   = xmsg.dest.net;
    msg->destAddr.node  = xmsg.dest.node;
    msg->destAddr.point = xmsg.dest.point;

    strcpy((char *)msg->datetime, (char *) xmsg.__ftsc_date);
    xstrcat(&(msg->subjectLine), (char *) xmsg.subj);
    xstrcat(&(msg->toUserName), (char *) xmsg.to);
    xstrcat(&(msg->fromUserName), (char *) xmsg.from);

    msg->textLength = MsgGetTextLen(SQmsg);
    xstralloc(&(msg->text),msg->textLength+1);
    MsgReadMsg(SQmsg, NULL, 0, msg->textLength, (unsigned char *) msg->text, 0, NULL);
    msg->text[msg->textLength] = '\0';

}

void afix(s_addr addr, char *cmd)
{
    HAREA           netmail;
    HMSG            SQmsg;
    unsigned long   highmsg, i;
    XMSG            xmsg;
    s_addr          dest;
    s_message	    msg, *tmpmsg;
    int             k, startarea = 0, endarea = config->netMailAreaCount;
    s_area          *area;
    char            *name = config->robotsArea;
    s_link          *link;

    w_log(LL_INFO, "Start AreaFix...");

    if ((area = getNetMailArea(config, name)) != NULL) {
	startarea = area - config->netMailAreas;
	endarea = startarea + 1;
    }

    if (cmd) {
	link = getLinkFromAddr(config, addr);
	if (link) {
	  if (cmd && strlen(cmd)) {
	    tmpmsg = makeMessage(&addr, link->ourAka, link->name,
				 link->RemoteRobotName ?
				 link->RemoteRobotName : "Areafix",
				 link->areaFixPwd ?
				 link->areaFixPwd : "", 1,
                 config->areafixKillReports);
	    tmpmsg->text = cmd;
	    processAreaFix(tmpmsg, NULL, 1);
	    tmpmsg->text=NULL;
	    freeMsgBuffers(tmpmsg);
	  } else w_log(LL_WARN, "areafix: empty areafix command from %s", aka2str(addr));
	} else w_log(LL_ERR, "areafix: no such link in config: %s!", aka2str(addr));
    }

    else for (k = startarea; k < endarea; k++) {
		
	netmail = MsgOpenArea((unsigned char *) config->netMailAreas[k].fileName,
			      MSGAREA_NORMAL, 
			      /*config -> netMailArea.fperm, 
				config -> netMailArea.uid,
				config -> netMailArea.gid,*/
			      (word)config -> netMailAreas[k].msgbType);

	if (netmail != NULL) {

	    highmsg = MsgGetHighMsg(netmail);
	    w_log(LL_INFO,"Scanning %s",config->netMailAreas[k].areaName);

	    // scan all Messages and test if they are already sent.
	    for (i=1; i<= highmsg; i++) {
		SQmsg = MsgOpenMsg(netmail, MOPEN_RW, i);

		// msg does not exist
		if (SQmsg == NULL) continue;

		MsgReadMsg(SQmsg, &xmsg, 0, 0, NULL, 0, NULL);
		cvtAddr(xmsg.dest, &dest);
                
		// if not read and for us -> process AreaFix
		striptwhite((char*)xmsg.to);
		if (((xmsg.attr & MSGREAD) != MSGREAD) && 
		    (isOurAka(config,dest)) && (strlen((char*)xmsg.to)>0) &&
		    ((stricmp((char*)xmsg.to, "areafix")==0) ||
		     (stricmp((char*)xmsg.to, "areamgr")==0) ||
		     (stricmp((char*)xmsg.to, "hpt")==0) ||
		     hpt_stristr(config->areafixNames,(char*)xmsg.to))) {
		    memset(&msg,'\0',sizeof(s_message));
		    MsgToStruct(SQmsg, xmsg, &msg);
		    processAreaFix(&msg, NULL, 0);
		    if (config->areafixKillRequests) {
			MsgCloseMsg(SQmsg);
			MsgKillMsg(netmail, i--);
		    } else {
			xmsg.attr |= MSGREAD;
			MsgWriteMsg(SQmsg, 0, &xmsg, NULL, 0, 0, 0, NULL);
			MsgCloseMsg(SQmsg);
		    }
		    freeMsgBuffers(&msg);
		}
		else MsgCloseMsg(SQmsg);

	    }

	    MsgCloseArea(netmail);
	} else {
	    w_log(LL_ERR, "Could not open %s", config->netMailAreas[k].areaName);
	}
    }
}

int unsubscribeFromPausedEchoAreas(s_link *link) {
    unsigned i,j;
    char *text = NULL;
    s_area *area;
    s_message *tmpmsg;

    for (i=0; i<config->echoAreaCount; i++) {
	area = &(config->echoAreas[i]);

	if ((area->msgbType & MSGTYPE_PASSTHROUGH) && isLinkOfArea(link,area)) {

	    // unsubscribe only if uplink & auto-paused downlink presents
	    if (area->downlinkCount==2) {
		if ((j = isAreaLink(link->hisAka, area)) != -1) {
		    // don't touch mandatory links
		    if (area->downlinks[j]->mandatory) continue;
		    // add area for unsubscribe
		    xstrscat(&text,"-",area->areaName,"\r",NULL);
		}
	    }
	}
    }

    if (text) {
	tmpmsg = makeMessage(&(link->hisAka), link->ourAka, link->name,
			     "areafix", link->areaFixPwd, 1,
                 config->areafixKillReports);
	tmpmsg->text = text;
	processAreaFix(tmpmsg, NULL, 0);
	freeMsgBuffers(tmpmsg);
	nfree(tmpmsg);
    }

    return 0;
}

void autoPassive()
{
  time_t   time_cur, time_test;
  struct   stat stat_file;
  s_message *msg;
  FILE *f;
  char *line, *path;
  unsigned int i;

  for (i = 0; i < config->linkCount; i++) {

      if (config->links[i].autoPause==0 || (config->links[i].Pause == (EPAUSE|FPAUSE))
         ) continue;

      if (createOutboundFileName(&(config->links[i]),
				 config->links[i].echoMailFlavour,
				 FLOFILE) == 0) {
	  f = fopen(config->links[i].floFile, "rt");
	  if (f) {
	      while ((line = readLine(f)) != NULL) {
		  line = trimLine(line);
		  path = line;
		  if (!isarcmail(path)) {
		      nfree(line);
		      continue;
		  }
		  if (*path && (*path == '^' || *path == '#')) {
		      path++;
		      // set Pause if files stored only in outbound
		      if (*path && strncmp(config->outbound,path,strlen(config->outbound)-1)==0 && stat(path, &stat_file) != -1) {

			  time_cur = time(NULL);
			  if (time_cur > stat_file.st_mtime) {
			      time_test = (time_cur - stat_file.st_mtime)/3600;
			  } else { // buggly time on file, anyway don't autopause on it
			      time_test = 0;
			  }

			  if (time_test >= (time_t)(config->links[i].autoPause*24)) {
			      w_log(LL_AREAFIX, "autopause: the file %s is %d days old", path, time_test/24);
			      if (Changepause((cfgFile) ? cfgFile :
					      getConfigFileName(),
					      &(config->links[i]), 1,
					      config->links[i].Pause^(EPAUSE|FPAUSE))) {
				  msg = makeMessage(config->links[i].ourAka,
						    &(config->links[i].hisAka),
						    versionStr,config->links[i].name,
						    "AutoPassive", 1,
                            config->areafixKillReports);
				  msg->text = createKludges(
                                config->disableTID,NULL,
							    config->links[i].ourAka,
							    &(config->links[i].hisAka)
                                ,versionStr);
				  xstrcat(&msg->text, "\r System switched to passive, your subscription are paused.\r\r"
					" You are being unsubscribed from echo areas with no downlinks besides you!\r\r"
					" When you wish to continue receiving echomail, please send requests\r"
					" to AreaFix containing the %RESUME command.");
				  xscatprintf(&msg->text, "\r\r--- %s autopause\r", versionStr);
				  msg->textLength = strlen(msg->text);
				  processNMMsg(msg, NULL,
					       getNetMailArea(config,config->robotsArea),
					       0, MSGLOCAL);
				  closeOpenedPkt();
				  freeMsgBuffers(msg);
				  nfree(msg);

				  // unsubscribe link from areas without non-paused links
				  unsubscribeFromPausedEchoAreas(&(config->links[i]));
			      } // end changepause
			      nfree(line);
			      //fclose(f); file closed after endwhile
			      break;
			  }
		      } /* endif */
		  } /* endif ^# */
		  nfree(line);
	      } /* endwhile */
	      fclose(f);
	  } /* endif */
	  nfree(config->links[i].floFile);
	  remove(config->links[i].bsyFile);
	  nfree(config->links[i].bsyFile);
      }
      nfree(config->links[i].pktFile);
      nfree(config->links[i].packFile);
  } /* endfor */
}

int relink (char *straddr) {
    s_link          *researchLink = NULL;
    unsigned int    count, areasArraySize;
    s_area          **areasIndexArray = NULL;
    struct _minf m;

    // parse config
    if (config==NULL) processConfig();
    if ( initSMAPI == -1 ) {
	// init SMAPI
	initSMAPI = 0;
	m.req_version = 0;
	m.def_zone = (UINT16) config->addr[0].zone;
	if (MsgOpenApi(&m) != 0) {
	    exit_hpt("MsgApiOpen Error",1);
	}
    }

    w_log(LL_START, "Start relink...");

    if (straddr) researchLink = getLink(config, straddr);
    else {
	w_log(LL_ERR, "No address");
	return 1;
    }

    if ( researchLink == NULL ) {
	w_log(LL_ERR, "Unknown link address %s", straddr);
	return 1;
    }

    areasArraySize = 0;
    areasIndexArray = (s_area **) safe_malloc
	(sizeof(s_area *) * (config->echoAreaCount + config->localAreaCount + 1));

    for (count = 0; count < config->echoAreaCount; count++)
	if ( isLinkOfArea(researchLink, &config->echoAreas[count])) {
	    areasIndexArray[areasArraySize] = &config->echoAreas[count];
	    areasArraySize++;
	    w_log(LL_AREAFIX, "Echo %s from link %s refreshed",
		  config->echoAreas[count].areaName, aka2str(researchLink->hisAka));
	}

    if ( areasArraySize > 0 ) {
	s_message *msg;

	msg = makeMessage(researchLink->ourAka,
			  &researchLink->hisAka,
			  versionStr,
			  researchLink->RemoteRobotName ?
			  researchLink->RemoteRobotName : "areafix",
			  researchLink->areaFixPwd ? researchLink->areaFixPwd : "", 1,
              config->areafixKillReports);

	msg->text = createKludges(config->disableTID,NULL,researchLink->ourAka,
                              &researchLink->hisAka,versionStr);

	for ( count = 0 ; count < areasArraySize; count++ ) {
	    if ((areasIndexArray[count]->downlinkCount  <= 1) &&
		(areasIndexArray[count]->msgbType & MSGTYPE_PASSTHROUGH))
		xscatprintf(&(msg->text), "-%s\r",areasIndexArray[count]->areaName);
	    else
		xscatprintf(&(msg->text), "+%s\r",areasIndexArray[count]->areaName);
	}

	xscatprintf(&(msg->text), " \r--- %s areafix\r", versionStr);
	msg->textLength = strlen(msg->text);
	w_log(LL_AREAFIX, "'Refresh' message created to `%s`",
	      researchLink->RemoteRobotName ?
	      researchLink->RemoteRobotName : "areafix");
	processNMMsg(msg, NULL,
		     getNetMailArea(config,config->robotsArea),
		     1, MSGLOCAL|MSGKILL);
	closeOpenedPkt();
	freeMsgBuffers(msg);
	nfree(msg);
	w_log(LL_AREAFIX, "Total request relink %i area(s)",areasArraySize);
    }

    nfree(areasIndexArray);

    return 0;
}
