/*
* hptkill - areas killer for high Portable Tosser (hpt)
* by Serguei Revtov 2:5021/11.10 || 2:5021/19.1
* Some code was taken from hpt/src/areafix.c
*
* 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: hptkill.c,v 1.23.2.7 2003/05/30 08:45:59 sfpavel Exp $
*/
#include <stdio.h>
#include <ctype.h>
#ifdef UNIX
#include <unistd.h>
#include <strings.h>
#else
#include <io.h>
#endif
#include <fcntl.h>
#ifdef __EMX__
#include <share.h>
#include <sys/types.h>
#endif
#include <sys/stat.h>
#ifdef __WATCOMC__
#include <fcntl.h>
#define AW_S_ISDIR(a) (((a) & S_IFDIR) != 0)
#include <process.h>
#include <dos.h>
#endif
#include <fcntl.h>
#include <errno.h>
#if defined(__TURBOC__) || defined(__IBMC__) || (defined(_MSC_VER) && (_MSC_VER >= 1200))
#include <io.h>
#include <fcntl.h>
#if !defined(S_ISDIR)
#define S_ISDIR(a) (((a) & S_IFDIR) != 0)
#endif
#endif
#include <time.h>
#include <smapi/msgapi.h>
#include <smapi/progprot.h>
#include <fidoconf/fidoconf.h>
#include <fidoconf/common.h>
#include <fidoconf/xstr.h>
#include <string.h>
#if defined ( __WATCOMC__ )
#include <smapi/prog.h>
#include <share.h>
#endif
#include <stdlib.h>
s_fidoconfig *config;
FILE *outlog;
char *version = "1.10.4-release";
typedef enum senduns { eNobody, eFirstLink, eNodes, eAll} e_senduns;
e_senduns sendUnSubscribe = eNodes;
int delFromConfig = 0;
int eraseBase = 1;
int killSend = 0;
int killPass = 0;
int createDupe = 0;
typedef struct xmsgtxt {
XMSG xmsg;
char *text;
} s_xmsgtxt;
void usage(void) {
fprintf(outlog, "hptkill %s\n", version);
fprintf(outlog, "Areas killing utility\n");
fprintf(outlog, "Usage:\n hptkill [options] [areaNameMask ...]\n");
fprintf(outlog, " -c config-file - specify alternate config file\n");
fprintf(outlog, " -1 - send unsubscribe message to first link only\n");
fprintf(outlog, " -n - don't send unsubscribe message\n");
fprintf(outlog, " -a - send unsubscribe message all subscribed links\n");
fprintf(outlog, " -d - delete area from config\n");
fprintf(outlog, " -s - save (don't erase) message & dupe bases\n");
fprintf(outlog, " -f file - read areas list from file in addition to args\n");
fprintf(outlog, " -f - - read areas list from stdin in addition to args\n");
fprintf(outlog, " -k - set Kill/Sent attribute to messages for links\n");
fprintf(outlog, " -p - find & kill passthrough echoareas with <=1 links\n");
fprintf(outlog, " -pp - same as -p including paused links\n");
fprintf(outlog, " -y - find & kill ANY echoareas with <=1 links\n");
fprintf(outlog, " -yp - same as -y including paused links\n");
fprintf(outlog, " -o days - kill passthrough area with dupebase older 'days' days\n");
fprintf(outlog, " -O days - same as -o but kill areas without dupebases\n");
fprintf(outlog, " -l file - with -o/-O write to file list of areas without dupebase\n");
fprintf(outlog, " -C - create empty dupebase if it doesn't exist\n");
fprintf(outlog, "\nDefault settings:\n");
fprintf(outlog, " - send unsubscribe message to subcribed nodes only\n");
fprintf(outlog, " - leave config unchanged\n");
fprintf(outlog, " - erase message & dupe bases\n");
exit(-1);
}
void exit_err(char *text)
{
fprintf(outlog, "exiting: %s\n", text);
exit(1);
}
#if defined(__TURBOC__) || defined(__IBMC__) || defined(__WATCOMC__) || (defined(_MSC_VER) && (_MSC_VER >= 1200))
int truncate(const char *fileName, long length)
{
int fd = open(fileName, O_RDWR | O_BINARY);
if (fd != -1) {
lseek(fd, length, SEEK_SET);
chsize(fd, tell(fd));
close(fd);
return 1;
};
return 0;
}
int fTruncate( int fd, long length )
{
if( fd != -1 )
{
lseek(fd, length, SEEK_SET);
chsize(fd, tell(fd) );
return 1;
}
return 0;
}
#endif
#ifdef __MINGW32__
int fTruncate (int fd, long length)
{
if( fd != -1 )
{
lseek(fd, length, SEEK_SET);
chsize(fd, tell(fd) );
return 1;
}
return 0;
}
#endif
int delareafromconfig(char *fileName, s_area *area) {
FILE *f;
char *cfgline, *token, *areaName, *buff;
long pos=-1, lastpos, endpos, len;
areaName = area->areaName;
if (init_conf(fileName)) return 1;
while ((buff = configline()) != NULL) {
buff = trimLine(buff);
buff = stripComment(buff);
if (buff[0] != 0) {
buff = cfgline = shell_expand(buff);
token = strseparate(&cfgline, " \t");
if (stricmp(token, "echoarea")==0) {
token = strseparate(&cfgline, " \t");
if (stricmp(token, areaName)==0) {
fileName = sstrdup(getCurConfName());
pos = getCurConfPos();
break;
}
}
}
nfree(buff);
}
close_conf();
if (pos == -1) return 1; // impossible
nfree(buff);
if ((f=fopen(fileName,"r+b")) == NULL)
{
fprintf(outlog, "\ncannot open config file %s \n", fileName);
nfree(fileName);
return 1;
}
fseek(f, pos, SEEK_SET);
cfgline = readLine(f);
if (cfgline == NULL) {
fclose(f);
nfree(fileName);
return 1;
}
fprintf(outlog, " %s...", fileName);
lastpos = ftell(f);
fseek(f, 0, SEEK_END);
endpos = ftell(f);
if (endpos>lastpos) {
buff = (char*) smalloc((size_t) (endpos-lastpos));
memset(buff, '\0', (size_t) (endpos-lastpos));
fseek(f, lastpos, SEEK_SET);
len = fread(buff, sizeof(char), (size_t) endpos-lastpos, f);
fseek(f, pos, SEEK_SET);
fwrite(buff, sizeof(char), (size_t) len, f);
nfree(buff);
} else {
len=0;
}
#if defined(__WATCOMC__) || defined(__MINGW32__)
fflush( f );
fTruncate( fileno(f), pos+len);
fflush( f );
#else
truncate(fileName, pos+len);
#endif
nfree(cfgline);
nfree(fileName);
fclose(f);
return 0;
}
int putMsgInArea(s_area *echo, XMSG *xmsg, char *text)
{
char *ctrlBuff, *textStart, *textWithoutArea;
UINT textLength;
HAREA harea;
HMSG hmsg;
char *slash;
int rc = 0;
// create Directory Tree if necessary
if (echo->msgbType == MSGTYPE_SDM)
_createDirectoryTree(echo->fileName);
else if (echo->msgbType==MSGTYPE_PASSTHROUGH) {
fprintf(outlog, "Can't put message to passthrough area %s!",
echo->areaName);
return rc;
} else {
// squish or jam area
slash = strrchr(echo->fileName, PATH_DELIM);
if (slash) {
*slash = '\0';
_createDirectoryTree(echo->fileName);
*slash = PATH_DELIM;
}
}
harea = MsgOpenArea((UCHAR *) echo->fileName, MSGAREA_CRIFNEC, (word)(echo->msgbType));
if (harea != NULL) {
hmsg = MsgOpenMsg(harea, MOPEN_CREATE, 0);
if (hmsg != NULL) {
textWithoutArea = text;
textLength = strlen(text);
ctrlBuff = (char *) CopyToControlBuf((UCHAR *) textWithoutArea,
(UCHAR **) &textStart,
&textLength);
// textStart is a pointer to the first non-kludge line
MsgWriteMsg(hmsg, 0, xmsg, (byte *) textStart, (dword) strlen(textStart), (dword) strlen(textStart), (dword)strlen(ctrlBuff), (byte *)ctrlBuff);
MsgCloseMsg(hmsg);
nfree(ctrlBuff);
rc = 1;
} else {
fprintf(outlog, "Could not create new msg in %s!", echo->fileName);
} /* endif */
MsgCloseArea(harea);
} else {
fprintf(outlog, "Could not open/create Area %s!", echo->fileName);
} /* endif */
return rc;
}
int makeRequestToLink (char *areatag, s_link *link) {
s_xmsgtxt *xmsgtxt;
XMSG *xmsg;
time_t curTime;
static time_t preTime=0L;
struct tm *date;
union stamp_combo dosdate;
if (link->hisAka.point)
fprintf(outlog, " Make message for %u:%u/%u.%u...",
link->hisAka.zone ,
link->hisAka.net ,
link->hisAka.node ,
link->hisAka.point);
else
fprintf(outlog, " Make message for %u:%u/%u...",
link->hisAka.zone ,
link->hisAka.net ,
link->hisAka.node);
if (link->msg == NULL) {
xmsgtxt = (s_xmsgtxt *) scalloc( 1, sizeof(s_xmsgtxt));
link->msg = xmsgtxt;
xmsg = &(xmsgtxt->xmsg);
xmsg->orig.zone = link->ourAka->zone ;
xmsg->orig.net = link->ourAka->net ;
xmsg->orig.node = link->ourAka->node ;
xmsg->orig.point = link->ourAka->point;
xmsg->dest.zone = link->hisAka.zone ;
xmsg->dest.net = link->hisAka.net ;
xmsg->dest.node = link->hisAka.node ;
xmsg->dest.point = link->hisAka.point;
strcpy( (char*)(xmsg->from), (char*)(config->sysop) );
strcpy( (char*)(xmsg->to), (char*)(link->RemoteRobotName ? link->RemoteRobotName : "AreaFix") );
strcpy( (char*)(xmsg->subj), (char*)(link->areaFixPwd ? link->areaFixPwd : "\0") );
xmsg->attr = MSGLOCAL|MSGPRIVATE;
if (killSend) xmsg->attr |= MSGKILL;
if (xmsg->orig.point) xscatprintf(&(xmsgtxt->text), "\001FMPT %d\r", xmsg->orig.point);
if (xmsg->dest.point) xscatprintf(&(xmsgtxt->text), "\001TOPT %d\r", xmsg->dest.point);
curTime = time(NULL);
while (curTime == preTime) {
sleep(1);
curTime = time(NULL);
}
preTime = curTime;
if (link->ourAka->point)
xscatprintf(&(xmsgtxt->text), "\001MSGID: %u:%u/%u.%u %08lx\r",
link->ourAka->zone,
link->ourAka->net,
link->ourAka->node,
link->ourAka->point,
(unsigned long) curTime);
else
xscatprintf(&(xmsgtxt->text), "\001MSGID: %u:%u/%u %08lx\r",
link->ourAka->zone,
link->ourAka->net,
link->ourAka->node,
(unsigned long) curTime);
date = localtime(&curTime);
fts_time(xmsg->__ftsc_date, date);
ASCII_Date_To_Binary(xmsg->__ftsc_date, (union stamp_combo *) &(xmsg->date_written));
TmDate_to_DosDate(date, &dosdate);
xmsg->date_arrived = dosdate.msg_st;
} else {
fprintf(outlog, "adding...");
xmsgtxt = (s_xmsgtxt *) link->msg;
}
xscatprintf(&(xmsgtxt->text), "-%s\r", areatag);
fprintf(outlog, "done\n");
return 0;
}
char *createDupeFileName(s_area *area) {
char *name=NULL, *afname, *retname=NULL;
if (!area->DOSFile) {
xstrcat(&name, area->areaName);
} else {
if (area->fileName)
xstrcat(&name,(afname=strrchr(area->fileName,PATH_DELIM))
? afname+1 : area->fileName);
else xstrcat(&name, "passthru");
}
switch (config->typeDupeBase) {
case hashDupes:
xstrcat(&name,".dph");
break;
case hashDupesWmsgid:
xstrcat(&name,".dpd");
break;
case textDupes:
xstrcat(&name,".dpt");
break;
case commonDupeBase:
break;
}
if (config->areasFileNameCase == eUpper)
name = strUpper(name);
else
name = strLower(name);
xstrscat(&retname, config->dupeHistoryDir, name, NULL);
nfree(name);
return retname;
}
void husky_delete_area(s_area *area)
{
char *an = area->areaName;
unsigned int i;
int rc;
fprintf(outlog, "Kill area %s\n", an);
switch (sendUnSubscribe) {
case eNobody:
break;
case eFirstLink:
makeRequestToLink(an, area->downlinks[0]->link);
break;
case eNodes:
for (i=0; i<area->downlinkCount; i++) {
if (area->downlinks[i]->link->hisAka.point == 0)
makeRequestToLink(an, area->downlinks[i]->link);
}
break;
case eAll:
for (i=0; i<area->downlinkCount; i++) {
makeRequestToLink(an, area->downlinks[i]->link);
}
break;
}
/* delete msgbase and dupebase for the area */
if (eraseBase) {
if (area->msgbType!=MSGTYPE_PASSTHROUGH) {
fprintf(outlog, " Removing base of %s...", an);
rc=MsgDeleteBase(area->fileName, (word) area->msgbType);
fprintf(outlog, "%s\n", rc ? "ok" : "unsuccessful");
}
if (area->dupeCheck != dcOff) {
char *dupename = createDupeFileName(area);
if (dupename) {
fprintf(outlog, " Removing dupes of %s...", an);
rc=unlink(dupename);
fprintf(outlog, "%s\n", rc==0 ? "ok" : "unsuccessful");
nfree(dupename);
}
}
}
/* remove area from config-file */
if (delFromConfig) {
fprintf(outlog, " deleting from config");
if (delareafromconfig (getConfigFileName(), area) != 0)
fprintf(outlog, " ERROR!\n");
else
fprintf(outlog, " ok\n");
/* 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--;
}
}
fprintf(outlog, "done\n"); // can't use an here - freed
}
int main(int argc, char **argv) {
int i, j, k;
struct _minf m;
char **areas = NULL;
char *needfree = NULL;
char *line = NULL;
int nareas=0;
int found = 0;
FILE *f = NULL;
s_xmsgtxt *xmsgtxt = NULL;
s_link *link = NULL;
int killed = 0;
int checkPaused = 0;
int killNoLink = 0;
int killLowLink = 0;
int killOld = 0;
int killWithoutDupes = 0;
int delArea = 0;
time_t oldest = 0;
s_area *area = NULL;
struct stat stbuf;
char *listNoDupeFile = NULL;
FILE *fNoDupe = NULL;
char *dupename = NULL;
char *cfgfile = NULL;
outlog=stdout;
setbuf(outlog, NULL);
for (i=1; i<argc; i++) {
if ( argv[i][0] == '-' ) {
switch (argv[i][1])
{
case 'c': /* alternate config file */
if ( ++i<argc ) {
cfgfile = argv[i];
} else {
fprintf( stderr, "Parameter required after -c\n");
usage();
}
break;
case '1': /* send unsubscribe message to first link only */
sendUnSubscribe = eFirstLink;
break;
case 'n': /* don't send unsubscribe message */
case 'N':
sendUnSubscribe = eNobody;
break;
case 'a': /* send unsubscribe message all links */
case 'A':
sendUnSubscribe = eAll;
break;
case 'd': /* delete area from config */
case 'D':
delFromConfig = 1;
break;
case 's': /* save (don't erase) message & dupe bases */
case 'S':
eraseBase = 0;
break;
case 'f': /* read areas list from file */
case 'F':
i++;
if ( argv[i] == NULL || argv[i][0] == '\0') {
usage();
exit(-1);
}
if (strcmp(argv[i], "-") == 0) {
f=stdin;
} else {
if ( !(f=fopen(argv[i], "r"))) {
fprintf(outlog, "%s: Can't open file '%s'\n", argv[0], argv[i]);
exit(-1);
}
}
while (!feof(f)) {
line = readLine(f);
if (line) {
nareas++;
areas = (char **)srealloc ( areas, nareas*sizeof(char *));
areas[nareas-1] = line;
needfree = (char *)srealloc ( needfree, nareas*sizeof(char));
needfree[nareas-1] = 1;
}
}
if (f != stdin) fclose(f);
break;
case 'k': /* set Kill/Sent attribute to messages for links */
case 'K':
killSend = 1;
break;
case 'p': /* kill passthrough areas with 1 link*/
case 'P':
killNoLink++;
killPass++;
if (argv[i][2]=='p' || argv[i][2]=='P') checkPaused++;
break;
case 'y': /* kill ANY areas with <=1 link*/
case 'Y':
killLowLink++;
if (argv[i][2]=='p' || argv[i][2]=='P') checkPaused++;
break;
case 'o': /* kill passthrough area with dupebase older 'days' days */
case 'O':
if (argv[i][1]=='O') killWithoutDupes++;
i++;
if ( argv[i] == NULL || argv[i][0] == '\0') {
usage();
exit(-1);
}
killPass++;
killOld++;
oldest = time(NULL) - atoi(argv[i]) * 60*60*24;
break;
case 'l': /* write list of areas without dupebase to file */
case 'L':
i++;
if ( argv[i] == NULL || argv[i][0] == '\0') {
usage();
exit(-1);
}
listNoDupeFile = argv[i];
break;
case 'C': /* create empty dupebase if it doesn't exist */
createDupe++;
break;
default:
usage();
exit(-1);
}
} else {
// AreaName(s) specified by args
nareas++;
areas = (char **)srealloc ( areas, nareas*sizeof(char *));
areas[nareas-1] = argv[i];
needfree = (char *)srealloc ( needfree, nareas*sizeof(char));
needfree[nareas-1] = 0;
}
}
if (nareas == 0) {
if (killPass || killLowLink) {
nareas++;
areas = (char **)srealloc ( areas, nareas*sizeof(char *));
areas[nareas-1] = "*";
needfree = (char *)srealloc ( needfree, nareas*sizeof(char));
needfree[nareas-1] = 0;
} else {
if (!createDupe) {
usage();
exit (-1);
}
}
}
fprintf(outlog,"hptkill %s\n", version);
if( cfgfile && cfgfile[0] )
config = readConfig(cfgfile);
else config = readConfig(getConfigFileName());
if (!config) {
fprintf(outlog, "Could not read fido config\n");
return (1);
}
m.req_version = 0;
m.def_zone = (UINT16) config->addr[0].zone;
if (MsgOpenApi(&m) != 0) fprintf(outlog, "MsgApiOpen Error");
for ( j=0; j<nareas; j++) {
found = 0;
for (i=0, area = config->echoAreas; (unsigned int)i < config->echoAreaCount; i++, area++) {
if (patimat(area->areaName, areas[j])==1){
delArea = 0;
if (killPass==0 && killLowLink==0) delArea++;
else if ((area->msgbType & MSGTYPE_PASSTHROUGH) == MSGTYPE_PASSTHROUGH) {
if (killNoLink) {
if (area->downlinkCount <= 1) delArea++;
else if (checkPaused) {
delArea = 2; // if two links w/o pause - leave untouched
for (k=0; (unsigned int)k < area->downlinkCount && delArea; k++) {
if (area->downlinks[k]->link->Pause == 0) delArea--;
}
}
}
if (killOld && !delArea && area->dupeCheck != dcOff) {
dupename = createDupeFileName(area);
if (dupename) {
if (stat(dupename, &stbuf)==0) {
if (stbuf.st_mtime < oldest) delArea++;
} else {
if (killWithoutDupes) {
delArea++;
}
if (listNoDupeFile) {
if (!fNoDupe) {
if (!(fNoDupe=fopen (listNoDupeFile, "a"))) {
fprintf (stderr, "Can't open file '%s' for appending\n", listNoDupeFile);
}
}
if (fNoDupe) fprintf (fNoDupe, "%s\n", area->areaName);
}
}
nfree(dupename);
}
}
}
if (killLowLink) {
if (area->downlinkCount <= 1) delArea++;
else if (checkPaused) {
delArea = 2; // if two links w/o pause - leave untouched
for (k=0; k < area->downlinkCount && delArea; k++) {
if (area->downlinks[k]->link->Pause == 0) delArea--;
}
}
}
if (delArea) {
husky_delete_area(area);
killed++;
found++;
if (delFromConfig) { // Area is removed from areas array!
i--;
area--;
}
}
}
}
if (killPass==0 && killLowLink==0) {
for (i=0, area=config->localAreas; (unsigned int)i < config->localAreaCount; i++, area++) {
if (patimat(area->areaName, areas[j])==1){
husky_delete_area(area);
killed++;
found++;
if (delFromConfig) { // Area is removed from areas array!
i--;
area--;
}
}
}
if (!found) fprintf(outlog, "Couldn't find area \"%s\"\n", areas[j]);
}
}
if (fNoDupe) fclose (fNoDupe);
if (killed) fprintf(outlog, "\n");
// Put mail for links to netmail
for (i=0; (unsigned int)i < config->linkCount; i++) {
if (config->links[i].msg) {
link = &(config->links[i]);
if (link->hisAka.point)
fprintf(outlog, "Write message for %u:%u/%u.%u...",
link->hisAka.zone ,
link->hisAka.net ,
link->hisAka.node ,
link->hisAka.point);
else
fprintf(outlog, "Write message for %u:%u/%u...",
link->hisAka.zone ,
link->hisAka.net ,
link->hisAka.node);
xmsgtxt = (s_xmsgtxt *) link->msg;
putMsgInArea(&(config->netMailAreas[0]), &(xmsgtxt->xmsg), xmsgtxt->text);
nfree(link->msg);
fprintf(outlog, "done\n");
}
}
for ( j=0; j<nareas; j++) if (needfree[nareas-1]) nfree(areas[j]);
if (needfree) nfree(needfree);
if (areas) nfree(areas);
if (killed && config->echotosslog) {
f=fopen(config->echotosslog, "a");
if (f==NULL) {
fprintf(outlog, "\nCould not open or create EchoTossLogFile\n");
} else {
fprintf(f,"%s\n", config->netMailAreas[0].areaName);
fclose(f);
}
}
if (createDupe) {
for (i=0, area = config->echoAreas; (unsigned int)i < config->echoAreaCount; i++, area++) {
dupename = createDupeFileName(area);
if (stat(dupename, &stbuf)!=0) {
fprintf (outlog, "Creating %s\n", dupename);
f=fopen(dupename, "a");
if (f) {
fclose (f);
} else {
fprintf (outlog, "Can't create %s\n", dupename);
}
}
nfree(dupename);
}
}
// deinit SMAPI
MsgCloseApi();
disposeConfig(config);
fprintf(outlog,"Done\n");
return (0);
}
syntax highlighted by Code2HTML, v. 0.9.1